@case-framework/survey-assistant
v0.12.0
Published
Shared protocol, engine, and runtime for the CASE survey assistant.
Readme
CASE Survey Assistant
This workspace package exposes isolated protocol, engine, runtime, headless React, and optional localized UI entry points:
import {
surveyChangeSetSchema,
surveyAssistantTurnFinalSchema,
type SurveyChangeSet,
} from "@case-framework/survey-assistant/protocol";
import { buildCapabilityDigest } from "@case-framework/survey-assistant/protocol/digest";
import {
createSurveyAssistantEngineContext,
validateSurveyOperations,
} from "@case-framework/survey-assistant/engine";
import {
defaultSurveyAssistantCapabilities,
defaultSurveyAssistantCapabilitySet,
} from "@case-framework/survey-assistant/capabilities/default";
import {
createSurveyAssistantChatHandler,
createSurveyAssistantSurveyLifecycle,
createSurveyAssistantSurveyThreadMetadata,
type SurveyAssistantAuthorize,
} from "@case-framework/survey-assistant/server";
import {
createSurveyAssistantAgent,
type SurveyAssistantHistoryCompatibility,
} from "@case-framework/survey-assistant/server/agent";
import {
SURVEY_ASSISTANT_TOOL_IDS,
surveyAssistantTools,
} from "@case-framework/survey-assistant/server/tools";
import {
createSurveyAssistantTaskAgent,
createSurveyAssistantTaskHandler,
} from "@case-framework/survey-assistant/server/tasks";
import {
createSurveyAssistantRuntime,
parseSurveyAssistantConfigurationDocument,
parseSurveyAssistantEnvironment,
} from "@case-framework/survey-assistant/server/runtime";
import {
createSurveyAssistantPostgresRuntime,
createSurveyAssistantPostgresThreadDocumentRepository,
initializeSurveyAssistantPostgresStorage,
} from "@case-framework/survey-assistant/storage/postgres";
import {
createSurveyAssistantControllerBridge,
createSurveyAssistantTaskClient,
createSurveyAssistantThreadLauncher,
} from "@case-framework/survey-assistant/react/integration";
import { useSurveyAssistantModelCatalog } from "@case-framework/survey-assistant/react";
import {
ConnectedSurveyAssistantPanel,
SurveyAssistantPanel,
} from "@case-framework/survey-assistant/ui";/protocol is the browser-safe runtime owner for wire schemas and inferred
types. /protocol/digest keeps stable hashing independently tree-shakeable for
Survey UI consumers. /engine derives request-local survey context and owns
pure inspection, normalization, validation/simulation, expression compilation,
and turn collection. /capabilities/default provides the trusted, versioned
capabilities for the standard CASE editor registry.
/server owns the lightweight framework-agnostic Web chat handler, required
host authorization contract, request guardrails and context, thread-attachment
repository and model-selected native replay, turn-final stream, error/metrics hooks, and packaged
Markdown authoring references. /server/tools owns the standard
request-context tools, and /server/agent owns their Agent,
instructions, thread-scoped Observational Memory defaults, and optional
history-compatibility policy for custom memory setups. /server/runtime is the
high-level composition layer: it parses a versioned provider/model policy
document or the legacy injected environment, resolves the approved model catalog
(including optional Azure deployment discovery), applies same-provider chat,
task, and memory roles,
exposes standard Mastra agents and gateways, and creates the Web handlers. The
host still constructs the standard Mastra instance and owns
credentials, authorization, storage connection lifecycle, logging, and route
files.
/react owns the headless chat session, threads, validated
model-catalog loading, remembered model selection, and structural controller
proxy. /react/integration is the lightweight controller and one-shot task
entry for editor packages that do not need chat transport. /ui owns the
reusable panel, transcript, reasoning/tool presentation, prompt and attachment
controls, model picker, thread history, and complete English and Dutch defaults. ConnectedSurveyAssistantPanel
also owns model-catalog loading and remembered selection for the common case. Import
@case-framework/survey-assistant/ui.css once from the host root layout. The
stylesheet has no preflight and scopes generated utilities beneath the panel
root. A host can pass a partial localization catalog to extend or override the
defaults without supplying a complete catalog.
Hosts using /ui must install @base-ui/react@^1.8.0 alongside React and
React DOM. Base UI is an optional peer dependency so server and protocol consumers
do not need to install it. Use the same Base UI installation for the assistant,
survey player, editor, and host components.
<ConnectedSurveyAssistantPanel
api="/api/survey-assistant/chat"
applyPolicy="auto"
controller={assistantController}
language={language}
localizations={assistantLocalizations}
onModelChange={setSelectedModelId}
resourceId={authenticatedUserId}
surveyId={surveyId}
threadApi="/api/survey-assistant/threads"
/>Host UI can start a fresh assistant thread with an already-submitted prompt. The launcher queues requests made before the panel mounts, which supports replacing a prompt-only new-survey screen with the chat after submission:
const threadLauncher = useMemo(() => createSurveyAssistantThreadLauncher(), []);
const describeNewItem = (prompt: string, parentItemId: string, afterItemId?: string) => {
setAssistantOpen(true);
void threadLauncher.startThread({
prompt,
uiContext: {
activeSurface: "new-item",
focusItemId: parentItemId,
highlights: [
{
kind: "insertion-target",
itemId: parentItemId,
label: "Requested item position",
details: afterItemId ? `Insert after item ${afterItemId}` : "Insert as the first child",
},
],
},
});
};
return assistantOpen ? (
<ConnectedSurveyAssistantPanel
api="/api/survey-assistant/chat"
applyPolicy="auto"
controller={assistantController}
resourceId={authenticatedUserId}
threadApi="/api/survey-assistant/threads"
threadLauncher={threadLauncher}
/>
) : (
<NewSurveyPrompt onSubmit={(prompt) => describeNewItem(prompt, "root")} />
);startThread() resolves after the new thread has been selected and its first
turn has been accepted by the chat session. uiContext is layered over the
controller snapshot for that turn only; it does not mutate editor selection or
navigation state. Use typed highlights for transient targets such as an
insertion position.
The browser-supplied resourceId is request metadata, not authorization. A
production host must derive user, survey, and thread ownership in the server
handler's required authorize callback.
For the supported real-app setup, configuration reference, route/auth contract, attachment policy, apply settlement, observability, cleanup, and acceptance checklist, see Real-host integration.
The repository's Survey Editor host keeps its active configuration local. Start
from
apps/survey-editor/config/survey-assistant.example.yaml
and keep provider credentials in the environment variables named by
apiKeyEnv.
createSurveyAssistantSurveyLifecycle(...).forSurvey(surveyId, resourceId)
provides one shared thread identity, chat authorizer, panel-prop object, and
per-user cleanup method so those paths cannot drift onto different survey
metadata.
/protocol, /engine, and /capabilities/default may not import React,
Next.js, Mastra, AI SDK, survey-ui, or Node-only storage. /protocol has Zod
plus the narrow browser-safe survey-core/editor/colors runtime dependency;
/engine and /capabilities/default may use the headless survey-elements
domain and the full survey-core API. /server is
server-only and may import Mastra, AI SDK, and Node APIs, but never React,
Next.js, or survey-ui. The separate server entries prevent handler-only
consumers from eagerly loading Agent/Memory code. @case-framework/survey-editor-ui
owns the optional live-editor bridge, snapshot/apply functions, and snapshot-backed
one-shot provider because those functions must operate on its editor instance
and editor registry. The assistant package supplies the standard task Web handler
and browser executor adapter, so another host does not need to recreate Next
server actions or repeat the one-shot method mapping. This package never
imports survey-ui.
Custom element packages define a server-safe AssistantElementCapabilityAdapter
and attach it as assistantCapability on an ElementRegistryEntry. The entry's
define(body) supplies the canonical schema, ownership, slots, activation, and
computed dependencies. See Custom survey elements
for registration and capability contracts for the
optional inspection, validation, and preset hooks.
The composed editor registry validates and caches the attached capabilities
once. Server configuration imports ratingAssistantCapability directly and
adds it to the trusted capability set; it must not trust capability functions
received from a browser snapshot.
pnpm --filter @case-framework/survey-assistant check
pnpm --filter @case-framework/survey-assistant check:release
pnpm benchmark:survey-assistant-operationscheck typechecks and builds all entries, copies server references, verifies
their source and built import graphs, and runs typed unit/DOM tests plus the real
SDK streaming integration tests (with a scripted model, no provider requests). check:release also
builds the clean Next host fixture. benchmark:survey-assistant-operations
uses the current built engine output to profile maximum-size typed validation
and the raw-patch path against a synthetic 5,002-item survey; run build first
when source has changed. The optional Postgres entry accepts a host-owned
PostgresStore or store configuration; it does not read environment variables,
create hidden singletons, or own connection shutdown.
Test boundaries
testchecks fixtures and runs the Vitest source suite. Its tool-constructor mock deliberately bypasses SDK validation for handler-level negative inputs; the test TypeScript config reflects that mock and keeps schema input/output types. The separate productiontypecheckuses the real SDK types.test:integrationruns built output through nativenode:testwith the real Agent, tools, HTTP handler, stream adapter, transport, and session hook. Only model responses are scripted. Build changed package output first. The editor UI suite also feeds these real streamed candidates through its apply/undo boundary in a separate native ESM process so source-test aliases and mocks cannot replace the SDKs.test:postgresalso uses nativenode:testand requiresSURVEY_ASSISTANT_TEST_DATABASE_URL. Use a disposable PostgreSQL database: the test creates a unique schema and drops only that schema in cleanup. It tests isolation, BYTEA roundtrips, deduplication, and concurrent quotas against real PostgreSQL. Missing configuration fails explicitly; this opt-in test is not part of the database-freecheckcommand.- Live model evaluations remain opt-in. DOM tests exercise events but do not establish browser layout, focus, scrolling, or accessibility acceptance.
SURVEY_ASSISTANT_TEST_DATABASE_URL=postgresql://localhost/assistant_test \
pnpm --filter @case-framework/survey-assistant test:postgresIntegration Levels
Use the highest level that fits the host; the lower layers remain available for custom authentication, provider policy, UI, or storage.
- Standard server:
parseSurveyAssistantConfigurationDocumentpluscreateSurveyAssistantRuntime, then register itsagentsandgatewaysin a normalMastrainstance and callruntime.createHandlers(...). - Custom server: compose
createSurveyAssistantAgent, individual chat, task, thread, and model handlers, and a customresolveModeldirectly. - Standard UI:
ConnectedSurveyAssistantPanelplus Survey UI'suseSurveyEditorAssistantIntegrationbridge. - Custom UI: use
useSurveyAssistantSession, thread/model clients from/react, and controller/task adapters from/react/integration.
The high-level APIs are thin composition helpers over the same exported primitives. They do not introduce a second runtime or hide Mastra.
Importing hosts can optionally require the additional
SURVEY_ASSISTANT_ALLOW_EXPERIMENTAL_IDENTITY=true production rollout gate by
passing requireExperimentalIdentityOptIn: true to a configuration parser.
The option does not choose or authorize an identity, and most authenticated
hosts should omit it unless they want that extra deployment switch. See
Real-host integration
for the exact enablement rules.
Response-variable authoring (survey schema version 3)
Assistant mutations target exact persistent item IDs. Search exposes resolved editor names and breadcrumbs for discovery; names may repeat. Response-slot declarations from the shared element definitions provide persistent IDs, coding names, domains, matrix ownership and computed dependencies. Expressions serialize references as {slotId, method: "get" | "isDefined"}.
Use update-response-settings with naming and settings arrays for atomic scalar names, matrix naming components, option export codes and export defaults. Shared core previews validate names, derived families and projected headers; editor application remains a single normal undoable transaction. The built-in registry supports consent, choice and form matrices alongside scalar inputs and option follow-up content.
One-shot tasks are suggest-response-name (a scalar itemId and slotId) and suggest-response-settings (slotIds and a naming category). Response Settings offers selected-variable or filtered-visible-variable suggestions, validates a before/after preview, and applies only on user action. Categories are fair, survey-pattern, compact, and preferred. All obey the same naming syntax; FAIR guidance is a project convention, not a certification.
Custom elements declare slots, ownership, activation and computed dependencies
through their trusted headless ElementDefinition, shared by the assistant and
player. Capability adapters supplement those declarations with authoring policy;
they do not supply constructors or a separate response schema. Pass the same capability set to buildSurveyAssistantTaskContext, or the editor AI provider/integration's optional capabilities, for custom-variable tasks. The server must continue to supply trusted capabilities independently of browser data.
