@svadmin/surface 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,6 +78,25 @@ import {
78
78
 
79
79
  When rendered inside `AdminApp`, the renderer resolves the configured provider for each resource. A trusted host may instead pass `dataProvider`, which is still narrowed to `getList` and `getOne`.
80
80
 
81
+ ## AI proposals
82
+
83
+ The DOM-free root entry also exposes an opt-in Agent protocol. `buildSurfaceAgentPrompt()` constrains a model to return a proposal envelope and includes the host's widget/resource/field allowlists. `parseSurfaceAgentProposal()` parses and validates the complete `SurfaceSpec` against the same catalog and policy without querying a provider:
84
+
85
+ ```ts
86
+ import {
87
+ buildSurfaceAgentPrompt,
88
+ parseSurfaceAgentProposal,
89
+ } from '@svadmin/surface';
90
+
91
+ const prompt = buildSurfaceAgentPrompt('Generate an inventory dashboard', catalog, policy);
92
+ const proposal = parseSurfaceAgentProposal(modelText, catalog, policy);
93
+ if (proposal.ok) {
94
+ // Preview proposal.value.spec, then require explicit user approval before rendering.
95
+ }
96
+ ```
97
+
98
+ The adapter is deliberately proposal-only. Persistence, revision history, audit records, and the final apply decision belong to the host application. It never executes generated code or mutation actions.
99
+
81
100
  ## Built-in catalog
82
101
 
83
102
  | Type | Binding | Purpose |
@@ -1,5 +1,5 @@
1
1
  {
2
- "surface": "0.6.x",
2
+ "surface": "0.7.x",
3
3
  "minimumSupported": {
4
4
  "@svadmin/core": "0.34.2",
5
5
  "@svadmin/ui": "0.40.6",
@@ -0,0 +1,23 @@
1
+ import type { SurfaceCatalog, SurfacePolicy, SurfaceSpec, SurfaceValidationIssue } from './types.js';
2
+ /** Wire version for model-produced, human-reviewable Surface proposals. */
3
+ export declare const SURFACE_AGENT_SCHEMA_VERSION: "surface-agent/v1";
4
+ export interface SurfaceAgentProposal {
5
+ readonly schemaVersion: typeof SURFACE_AGENT_SCHEMA_VERSION;
6
+ readonly action: 'propose';
7
+ readonly summary?: string;
8
+ readonly spec: SurfaceSpec;
9
+ }
10
+ export type SurfaceAgentValidationResult = {
11
+ readonly ok: true;
12
+ readonly value: SurfaceAgentProposal;
13
+ } | {
14
+ readonly ok: false;
15
+ readonly issues: readonly SurfaceValidationIssue[];
16
+ };
17
+ /**
18
+ * Parse and fully validate an AI-generated proposal before a host previews it.
19
+ * This function has no side effects and never queries a provider.
20
+ */
21
+ export declare function parseSurfaceAgentProposal(input: unknown, catalog: SurfaceCatalog, policy: SurfacePolicy): SurfaceAgentValidationResult;
22
+ /** Build a model instruction that keeps generation inside the Surface contract. */
23
+ export declare function buildSurfaceAgentPrompt(request: string, catalog: SurfaceCatalog, policy: SurfacePolicy): string;
package/dist/agent.js ADDED
@@ -0,0 +1,78 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { TypeCompiler } from '@sinclair/typebox/compiler';
3
+ import { jsonPointer } from './json.js';
4
+ import { validateSurfaceSpec } from './validation.js';
5
+ /** Wire version for model-produced, human-reviewable Surface proposals. */
6
+ export const SURFACE_AGENT_SCHEMA_VERSION = 'surface-agent/v1';
7
+ const proposalSchema = Type.Object({
8
+ schemaVersion: Type.Literal(SURFACE_AGENT_SCHEMA_VERSION),
9
+ action: Type.Literal('propose'),
10
+ summary: Type.Optional(Type.String({ minLength: 1, maxLength: 240 })),
11
+ spec: Type.Unknown(),
12
+ }, { additionalProperties: false });
13
+ const compiledProposalSchema = TypeCompiler.Compile(proposalSchema);
14
+ function parseCandidate(value) {
15
+ if (typeof value !== 'string')
16
+ return value;
17
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(value);
18
+ const source = fenced?.[1] ?? value.trim();
19
+ return JSON.parse(source);
20
+ }
21
+ function proposalIssue(message) {
22
+ return {
23
+ code: 'invalid_json',
24
+ path: '/',
25
+ message,
26
+ };
27
+ }
28
+ function proposalSchemaIssues(errors) {
29
+ return [...errors].map((issue) => ({
30
+ code: 'invalid_json',
31
+ path: issue.path || jsonPointer([]),
32
+ message: issue.message,
33
+ }));
34
+ }
35
+ /**
36
+ * Parse and fully validate an AI-generated proposal before a host previews it.
37
+ * This function has no side effects and never queries a provider.
38
+ */
39
+ export function parseSurfaceAgentProposal(input, catalog, policy) {
40
+ let candidate;
41
+ try {
42
+ candidate = parseCandidate(input);
43
+ }
44
+ catch {
45
+ return { ok: false, issues: [proposalIssue('Agent proposal must be valid JSON')] };
46
+ }
47
+ if (!compiledProposalSchema.Check(candidate)) {
48
+ return { ok: false, issues: proposalSchemaIssues(compiledProposalSchema.Errors(candidate)) };
49
+ }
50
+ const parsed = candidate;
51
+ const surface = validateSurfaceSpec(parsed.spec, catalog, policy);
52
+ if (!surface.ok)
53
+ return surface;
54
+ return {
55
+ ok: true,
56
+ value: {
57
+ schemaVersion: SURFACE_AGENT_SCHEMA_VERSION,
58
+ action: 'propose',
59
+ ...(parsed.summary === undefined ? {} : { summary: parsed.summary }),
60
+ spec: surface.value,
61
+ },
62
+ };
63
+ }
64
+ /** Build a model instruction that keeps generation inside the Surface contract. */
65
+ export function buildSurfaceAgentPrompt(request, catalog, policy) {
66
+ const widgetTypes = catalog.widgets.map((widget) => widget.type).join(', ') || '(none)';
67
+ const resources = Object.entries(policy.resources).map(([resource, resourcePolicy]) => {
68
+ const permissions = [
69
+ `read=${resourcePolicy.readFields.join(',') || '(none)'}`,
70
+ `filter=${resourcePolicy.filterFields?.join(',') || '(none)'}`,
71
+ `sort=${resourcePolicy.sortFields?.join(',') || '(none)'}`,
72
+ `getOne=${resourcePolicy.allowGetOne === true}`,
73
+ `maxPageSize=${resourcePolicy.maxPageSize ?? 'default'}`,
74
+ ];
75
+ return `${resource}(${permissions.join(';')})`;
76
+ }).join(' | ') || '(none)';
77
+ return `${request}\n\n[svadmin surface agent protocol]\nReturn only a human-reviewable fenced JSON proposal. Never generate or execute Svelte, HTML, CSS, JavaScript, SQL, URLs, event handlers, or mutations. The envelope must be {"schemaVersion":"${SURFACE_AGENT_SCHEMA_VERSION}","action":"propose","summary":"...","spec":{...}}. The spec must use schemaVersion "surface/v1" and catalogVersion "${catalog.version}". Allowed widget types: ${widgetTypes}. Resource policy: ${resources}. Use only catalog widgets and policy-authorized resources and fields. If the request cannot be represented safely, explain the limitation without inventing fields or capabilities.`;
78
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { SURFACE_LIMITS, SURFACE_SCHEMA_VERSION } from './types.js';
2
2
  export type { JsonObject, JsonPrimitive, JsonValue, ResourceListDataSource, ResourceListSource, ResourceOneDataSource, ResourceOneSource, SurfaceBinding, SurfaceCatalog, SurfaceCatalogDataKind, SurfaceDataError, SurfaceDataProvider, SurfaceDataSource, SurfaceFilter, SurfaceGridLayout, SurfaceGridSpan, SurfacePolicy, SurfaceResourcePolicy, SurfaceSort, SurfaceSpec, SurfaceValidationCode, SurfaceValidationIssue, SurfaceValidationResult, SurfaceWidget, SurfaceWidgetDataState, SurfaceWidgetDefinition, } from './types.js';
3
3
  export { validateSurfaceSpec } from './validation.js';
4
+ export { SURFACE_AGENT_SCHEMA_VERSION, buildSurfaceAgentPrompt, parseSurfaceAgentProposal, } from './agent.js';
5
+ export type { SurfaceAgentProposal, SurfaceAgentValidationResult, } from './agent.js';
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { SURFACE_LIMITS, SURFACE_SCHEMA_VERSION } from './types.js';
2
2
  export { validateSurfaceSpec } from './validation.js';
3
+ export { SURFACE_AGENT_SCHEMA_VERSION, buildSurfaceAgentPrompt, parseSurfaceAgentProposal, } from './agent.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/surface",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Validated declarative surfaces for svadmin dashboards",
5
5
  "type": "module",
6
6
  "sideEffects": false,