@synap-core/workspace-templates 0.9.3 → 0.10.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/dist/index.d.ts CHANGED
@@ -5,13 +5,13 @@
5
5
  * YAML source files validate against these types.
6
6
  * Every consumer (browser, backend, CLI, marketplace) imports from here.
7
7
  */
8
- type TemplateAppKind = 'native' | 'stored' | 'url';
8
+ type TemplateAppKind = "native" | "stored" | "url";
9
9
  interface TemplateAppRef {
10
10
  kind: TemplateAppKind;
11
11
  /** For kind='native': the appId registered in the browser manifest */
12
12
  appId?: string;
13
13
  /** For kind='native': how the platform renders the app */
14
- rendererType?: 'native' | 'external' | 'iframe-srcdoc';
14
+ rendererType?: "native" | "external" | "iframe-srcdoc";
15
15
  /** For kind='stored': the cell key registered in the cell registry */
16
16
  cellKey?: string;
17
17
  /** For kind='stored': default props passed to the cell */
@@ -25,25 +25,34 @@ type WorkspaceJsonValue = string | number | boolean | null | WorkspaceJsonValue[
25
25
  [key: string]: WorkspaceJsonValue;
26
26
  };
27
27
  type WorkspacePrimarySurface = {
28
- kind: 'app';
28
+ kind: "app";
29
29
  appId: string;
30
- rendererType: 'native';
30
+ rendererType: "native";
31
31
  title?: string;
32
32
  props?: Record<string, WorkspaceJsonValue>;
33
33
  } | {
34
- kind: 'app';
34
+ kind: "app";
35
35
  appId: string;
36
- rendererType: 'external';
36
+ rendererType: "external";
37
37
  url: string;
38
38
  title?: string;
39
39
  props?: Record<string, WorkspaceJsonValue>;
40
40
  } | {
41
- kind: 'cell';
41
+ kind: "cell";
42
42
  cellKey: string;
43
43
  title?: string;
44
44
  props?: Record<string, WorkspaceJsonValue>;
45
45
  } | {
46
- kind: 'view';
46
+ /**
47
+ * Ordinary website. Unlike an external `app`, this has no trusted app
48
+ * identity and is always opened through the Browser's unprivileged URL
49
+ * surface.
50
+ */
51
+ kind: "url";
52
+ url: string;
53
+ title?: string;
54
+ } | {
55
+ kind: "view";
47
56
  viewId: string;
48
57
  title?: string;
49
58
  };
@@ -53,12 +62,12 @@ type WorkspacePrimarySurface = {
53
62
  * slug. The pod resolves this to `WorkspacePrimarySurface` before persistence.
54
63
  */
55
64
  type WorkspacePrimarySurfaceViewReference = {
56
- kind: 'view';
65
+ kind: "view";
57
66
  viewName: string;
58
67
  viewSlug?: string;
59
68
  title?: string;
60
69
  } | {
61
- kind: 'view';
70
+ kind: "view";
62
71
  viewName?: string;
63
72
  viewSlug: string;
64
73
  title?: string;
@@ -102,11 +111,11 @@ interface TemplateProfile {
102
111
  * `entity_scope` of an existing profile (a divergence is reported as an
103
112
  * advisory `scopeConflict` and the live row is left alone).
104
113
  */
105
- entityScope?: 'pod' | 'workspace';
114
+ entityScope?: "pod" | "workspace";
106
115
  /** Semantic slug for system profiles. */
107
116
  semanticSlug?: string;
108
117
  /** Kind vs role profile. Omit for default ('kind'). */
109
- profileKind?: 'kind' | 'role';
118
+ profileKind?: "kind" | "role";
110
119
  /** For profileKind='role': which base-kind profile slugs this role can attach to. */
111
120
  applicableKinds?: string[];
112
121
  properties: TemplateProperty[];
@@ -129,6 +138,28 @@ interface TemplateEntityLink {
129
138
  type: string;
130
139
  label?: string;
131
140
  }
141
+ /**
142
+ * A domain-level relation TYPE definition — a `relation_defs` row, the workspace's
143
+ * relation vocabulary. Distinct from {@link TemplateEntityLink}, which is a
144
+ * profile→profile EDGE (and only carries `type` + `label`). A relation def stands
145
+ * alone: it has no source/target profile pairing and it carries the rich metadata
146
+ * (`description`, `isDirectional`, `uiHints`) that the entity-link path drops.
147
+ *
148
+ * Mirrors `DefaultRelationDef` in synap-backend
149
+ * `packages/database/src/utils/default-relation-defs.ts` VERBATIM — the door
150
+ * find-or-creates a `relation_defs` row from each entry (idempotent by
151
+ * `slug` within the workspace), which is `ensureDefaultRelationDefs`'s body.
152
+ */
153
+ interface TemplateRelationDef {
154
+ slug: string;
155
+ displayName: string;
156
+ description?: string;
157
+ isDirectional?: boolean;
158
+ uiHints?: {
159
+ category?: "workflow" | "social" | "reference" | "hierarchy";
160
+ inverseLabel?: string;
161
+ };
162
+ }
132
163
  interface TemplateSeedEntity {
133
164
  profileSlug: string;
134
165
  title: string;
@@ -159,21 +190,21 @@ interface TemplateDisplayTemplate {
159
190
  config: Record<string, unknown>;
160
191
  }
161
192
  interface TemplateSidebarSurface {
162
- kind: 'cell' | 'view' | 'entity' | 'document' | 'channel' | 'app' | 'url';
193
+ kind: "cell" | "view" | "entity" | "document" | "channel" | "app" | "url";
163
194
  cellKey?: string;
164
195
  viewId?: string;
165
196
  viewName?: string;
166
197
  appId?: string;
167
198
  url?: string;
168
- rendererType?: 'native' | 'external' | 'iframe-srcdoc';
169
- placement?: 'main' | 'side' | 'floating' | 'modal' | 'popover' | 'embed';
170
- displayMode?: 'compact' | 'medium' | 'full';
199
+ rendererType?: "native" | "external" | "iframe-srcdoc";
200
+ placement?: "main" | "side" | "floating" | "modal" | "popover" | "embed";
201
+ displayMode?: "compact" | "medium" | "full";
171
202
  props?: Record<string, unknown>;
172
203
  title?: string;
173
204
  meta?: Record<string, unknown>;
174
205
  }
175
206
  interface TemplateSidebarItem {
176
- kind: 'app' | 'view' | 'profile' | 'external' | 'cell';
207
+ kind: "app" | "view" | "profile" | "external" | "cell";
177
208
  appId?: string;
178
209
  viewName?: string;
179
210
  viewId?: string;
@@ -204,7 +235,7 @@ interface TemplateLayoutConfig {
204
235
  sidebarItems?: TemplateSidebarItem[];
205
236
  }
206
237
  interface TemplateBentoBlock {
207
- kind: 'view' | 'widget';
238
+ kind: "view" | "widget";
208
239
  viewName?: string;
209
240
  widgetType?: string;
210
241
  pos: {
@@ -222,7 +253,7 @@ interface TemplateProfileEntityBentoTemplates {
222
253
  };
223
254
  }
224
255
  /** Mirrors `WorkspaceSourceRole` in synap-backend `schema/workspaces.ts`. */
225
- type TemplateSourceRole = 'provider' | 'consumer' | 'provider-consumer';
256
+ type TemplateSourceRole = "provider" | "consumer" | "provider-consumer";
226
257
  /** Mirrors `WorkspaceDefaultSource` in synap-backend `schema/workspaces.ts`. */
227
258
  interface TemplateDefaultSource {
228
259
  /** The workspace id that supplies this capability domain. */
@@ -240,7 +271,7 @@ interface TemplateOnboardingCollectTarget {
240
271
  /** Human description of what to capture for this target. */
241
272
  what: string;
242
273
  /** Roughly how many to expect — guides interview depth. */
243
- cardinality?: 'one' | 'few' | 'several';
274
+ cardinality?: "one" | "few" | "several";
244
275
  /** Key fields the agent should make sure to fill. */
245
276
  keyFields?: string[];
246
277
  }
@@ -280,7 +311,7 @@ interface TemplateOnboarding {
280
311
  }
281
312
  interface TemplatePlaybookParam {
282
313
  name: string;
283
- type: 'text' | 'number' | 'entity' | 'choice' | 'boolean';
314
+ type: "text" | "number" | "entity" | "choice" | "boolean";
284
315
  label?: string;
285
316
  required?: boolean;
286
317
  defaultValue?: unknown;
@@ -290,12 +321,12 @@ interface TemplatePlaybook {
290
321
  description?: string;
291
322
  goalTemplate: string;
292
323
  params?: TemplatePlaybookParam[];
293
- executor?: 'is-agent' | 'external-agent' | 'hybrid';
294
- inputStrategy?: 'none' | 'static' | 'rotating' | 'query';
324
+ executor?: "is-agent" | "external-agent" | "hybrid";
325
+ inputStrategy?: "none" | "static" | "rotating" | "query";
295
326
  channelSpec?: {
296
- type: 'GROUP' | 'AGENT_COLLAB' | 'THREAD';
327
+ type: "GROUP" | "AGENT_COLLAB" | "THREAD";
297
328
  members?: string[];
298
- aiReactionMode?: 'on_mention' | 'always' | 'never';
329
+ aiReactionMode?: "on_mention" | "always" | "never";
299
330
  };
300
331
  /**
301
332
  * A cron schedule that maintains ONE backing cron automation (a single
@@ -327,29 +358,45 @@ interface TemplatePlaybook {
327
358
  profileSlug: string;
328
359
  filter?: Record<string, unknown>;
329
360
  };
330
- status?: 'draft' | 'active' | 'paused';
361
+ status?: "draft" | "active" | "paused";
331
362
  }
332
363
  interface TemplateAutomation {
333
364
  /** Stable local key (unused on the wire today — reserved for dedup/refs). */
334
365
  key?: string;
335
366
  name: string;
336
367
  description?: string;
337
- triggerType: 'event' | 'cron' | 'webhook' | 'manual';
368
+ triggerType: "event" | "cron" | "webhook" | "manual";
338
369
  triggerConfig?: Record<string, unknown>;
339
370
  flow: {
340
371
  nodes: unknown[];
341
372
  edges: unknown[];
342
373
  precondition?: string;
343
374
  };
344
- status?: 'draft' | 'active' | 'paused';
375
+ status?: "draft" | "active" | "paused";
376
+ }
377
+ interface TemplateCommandDerivedInput {
378
+ name: string;
379
+ label?: string;
380
+ /** Mirrors `DerivedInput.type` (text | number | entity | view | choice). */
381
+ type?: string;
382
+ }
383
+ interface TemplateCommand {
384
+ title: string;
385
+ promptTemplate: string;
386
+ derivedInputs?: TemplateCommandDerivedInput[];
387
+ /** text | proposal | view */
388
+ outputMode?: string;
389
+ canCreateViews?: boolean;
390
+ /** read_only | propose_writes */
391
+ permissionsProfile?: string;
345
392
  }
346
393
  interface TemplateActionPlacement {
347
394
  /** Entity kind whose detail surface this action appears on. */
348
395
  profileSlug: string;
349
396
  /** Where the action renders. Only "entity-detail" is consumed today. */
350
- surface: 'entity-detail' | string;
397
+ surface: "entity-detail" | string;
351
398
  /** What the action does when clicked. */
352
- kind: 'capability' | 'playbook' | 'automation';
399
+ kind: "capability" | "playbook" | "automation";
353
400
  /**
354
401
  * Reference to the thing to run: a capability verb key, or a playbook /
355
402
  * automation NAME (resolved to a row id at apply time).
@@ -381,7 +428,7 @@ interface TemplateIntegration {
381
428
  /** Override default params with {{var}} interpolation. */
382
429
  params?: Record<string, string>;
383
430
  /** "apply" (create+update) or "install" (create only). */
384
- mode?: 'apply' | 'install';
431
+ mode?: "apply" | "install";
385
432
  }
386
433
  interface TemplateExtendsRef {
387
434
  source: string;
@@ -465,7 +512,7 @@ interface TemplateDependency {
465
512
  * - `'require'` (default): the dependency must be present on the pod as its
466
513
  * own artifact, but this template does not merge into it.
467
514
  */
468
- relation?: 'compose' | 'require';
515
+ relation?: "compose" | "require";
469
516
  /**
470
517
  * Short human reason shown in the install-time "this also installs…" prompt.
471
518
  */
@@ -502,7 +549,7 @@ interface WorkspaceYaml {
502
549
  * Workspace classifier — consumed by createFromDefinition (settings.workspaceType,
503
550
  * creator-role + agent-workspace behavior). Mirrors the backend union.
504
551
  */
505
- workspaceType?: 'personal' | 'agent' | 'project' | 'operational';
552
+ workspaceType?: "personal" | "agent" | "project" | "operational";
506
553
  visibility?: string;
507
554
  /** Capabilities this workspace provides (e.g. "brand.library", "content.pipeline") */
508
555
  capabilities?: string[];
@@ -528,6 +575,19 @@ interface WorkspaceYaml {
528
575
  views?: TemplateView[];
529
576
  /** Schema-level links between entity types */
530
577
  entityLinks?: TemplateEntityLink[];
578
+ /**
579
+ * Domain-level relation TYPE vocabulary seeded into the workspace's
580
+ * `relation_defs`. Distinct from `entityLinks` (profile→profile edges): a
581
+ * relation def stands alone and carries `description`/`isDirectional`/`uiHints`.
582
+ * The `base` template carries the universal defaults. See {@link TemplateRelationDef}.
583
+ */
584
+ relationDefs?: TemplateRelationDef[];
585
+ /**
586
+ * Workspace-scoped intelligence commands (prompt-template "Default Command
587
+ * Pack") seeded with the workspace. The `base` template carries the universal
588
+ * pack. See {@link TemplateCommand}.
589
+ */
590
+ commands?: TemplateCommand[];
531
591
  /** Initial data to seed on workspace creation */
532
592
  seedEntities?: TemplateSeedEntity[];
533
593
  /** Relations between seed entities, created on provisioning */
@@ -968,9 +1028,11 @@ declare function layerTemplateGraph(input: LayerTemplateGraphInput): Composition
968
1028
  * Cross-template CONCEPT guard — catches vocabulary forks between templates.
969
1029
  *
970
1030
  * WHY THIS EXISTS (read before weakening or deleting it):
971
- * `business-developer.yaml` introduced workspace-scoped `crm-lead`/`crm-client`
972
- * for the same real-world concept that `foundation.yaml`/`crm.yaml` declare as
973
- * pod-wide shared `lead`/`client`. Nothing failed. Three reasons:
1031
+ * `business-developer.yaml` once introduced workspace-scoped
1032
+ * `crm-lead`/`crm-client` for the same real-world concept that
1033
+ * `foundation.yaml`/`crm.yaml` declare as pod-wide shared `lead`/`client` (that
1034
+ * fork has since been collapsed back onto the shared slugs). Nothing failed at
1035
+ * the time. Three reasons:
974
1036
  *
975
1037
  * 1. `build.mjs`'s `validate()` runs PER FILE, inside the read loop, so it
976
1038
  * structurally cannot see that another template already owns `lead`.
@@ -1008,6 +1070,13 @@ interface ConceptViolation {
1008
1070
  * list cannot rot into a graveyard.
1009
1071
  *
1010
1072
  * Key = the two colliding profile slugs, sorted, joined by "|".
1073
+ *
1074
+ * Currently EMPTY, and that is the healthy state. The two entries this list was
1075
+ * born for — `crm-lead|lead` and `client|crm-client` — were RESOLVED rather
1076
+ * than kept: business-developer.yaml now re-declares the shared `lead`/`client`
1077
+ * roles and carries its business-development fields as workspace-overlay
1078
+ * PROPERTIES. One concept, one slug, workspace-specific fields. Rule 3 below
1079
+ * would have failed CI had the entries been left behind.
1011
1080
  */
1012
1081
  declare const DECLARED_DIVERGENCES: Record<string, string>;
1013
1082
  /**
@@ -1020,7 +1089,14 @@ declare const DECLARED_DIVERGENCES: Record<string, string>;
1020
1089
  * is honest: no guard can know they mean the same thing.
1021
1090
  */
1022
1091
  declare function conceptTerm(semanticSlug: string): string;
1023
- declare function findConceptViolations(templates?: Record<string, WorkspaceYaml>): ConceptViolation[];
1092
+ declare function findConceptViolations(templates?: Record<string, WorkspaceYaml>,
1093
+ /**
1094
+ * The declared-divergence allowlist to enforce against. Defaults to the real
1095
+ * one; injectable so the escape-hatch semantics (rule 1 suppression, rule 3
1096
+ * staleness) stay testable now that the shipped list is empty — previously
1097
+ * those tests could only be written because a live entry happened to exist.
1098
+ */
1099
+ declaredDivergences?: Record<string, string>): ConceptViolation[];
1024
1100
  declare function formatConceptViolations(violations: ConceptViolation[]): string;
1025
1101
 
1026
1102
  /**
@@ -1253,18 +1329,18 @@ declare function validateTemplate(tpl: WorkspaceYaml, opts?: ValidateOptions): V
1253
1329
  * Until then: keep the constant arrays below identical to `@synap/database`
1254
1330
  * `schema/automations.ts`.
1255
1331
  */
1256
- /** The 21 node types — `AutomationNodeBase.type` union (schema/automations.ts). */
1257
- declare const FLOW_NODE_TYPES: readonly ["trigger", "command", "condition", "delay", "output", "loop", "transform", "fetch", "query", "entity_read", "related_entities", "guard", "compute", "select", "claim", "messages_query", "switch", "skill", "capability", "sub_automation", "playbook_run"];
1332
+ /** The 23 node types — `AutomationNodeBase.type` union (schema/automations.ts). */
1333
+ declare const FLOW_NODE_TYPES: readonly ["trigger", "command", "condition", "delay", "output", "loop", "transform", "fetch", "query", "entity_read", "related_entities", "guard", "compute", "select", "claim", "messages_query", "runs_query", "proposals_query", "switch", "skill", "capability", "sub_automation", "playbook_run"];
1258
1334
  /** The 11 output types — `OutputNodeDef.data.outputType` (schema/automations.ts). */
1259
1335
  declare const FLOW_OUTPUT_TYPES: readonly ["notification", "entity_create", "entity_update", "facet_attach", "facet_update", "facet_detach", "relation_create", "webhook", "channel_message", "session_update", "set_state"];
1260
1336
  /**
1261
1337
  * The known `config.channelType` values for a `channel_message` output. Mirrors
1262
1338
  * the executor's channel_message resolution (automation-executor.ts). The output
1263
- * channel is CONTEXT-DERIVED — a channel_message is valid with `channelId`, OR
1264
- * `channelEntityRef` (an entity-id expression → that entity's internal channel),
1265
- * OR a `channelType` from this set, OR NOTHING (the executor defaults a targetless
1266
- * channel_message to the automation's own run channel). A `channelType` that is
1267
- * PRESENT but not one of these is still a typo → still an error.
1339
+ * channel is now CONTEXT-DERIVED — a channel_message is valid with `channelId`,
1340
+ * OR `channelEntityRef` (an entity-id expression → that entity's internal
1341
+ * channel), OR a `channelType` from this set, OR NOTHING (the executor defaults a
1342
+ * targetless channel_message to the automation's own run channel). A `channelType`
1343
+ * that is PRESENT but not one of these is still a typo → still an error.
1268
1344
  */
1269
1345
  declare const CHANNEL_MESSAGE_CHANNEL_TYPES: readonly ["personal_thread", "proactive", "subjectEntity"];
1270
1346
  interface FlowValidationError {
@@ -1381,6 +1457,19 @@ interface WorkspaceDefinitionInput {
1381
1457
  seedEntities: NonNullable<WorkspaceYaml['seedEntities']>;
1382
1458
  suggestedRelations?: WorkspaceYaml['suggestedRelations'];
1383
1459
  displayTemplates?: WorkspaceYaml['displayTemplates'];
1460
+ /**
1461
+ * Domain-level relation TYPE vocabulary — the door find-or-creates a
1462
+ * `relation_defs` row per entry (idempotent by slug within the workspace). NOT
1463
+ * `entityLinks` (those are profile→profile edges and drop the rich metadata).
1464
+ * Carried verbatim; the authored form IS the wire form.
1465
+ */
1466
+ relationDefs?: WorkspaceYaml['relationDefs'];
1467
+ /**
1468
+ * Workspace intelligence commands — the door find-or-creates an
1469
+ * `intelligence_commands` row per entry (idempotent by title). Carried
1470
+ * verbatim; the authored form IS the wire form.
1471
+ */
1472
+ commands?: WorkspaceYaml['commands'];
1384
1473
  layoutConfig: WorkspaceYaml['layout'] | undefined;
1385
1474
  bentoViewName?: string;
1386
1475
  /**
@@ -1485,6 +1574,10 @@ interface PackageDefinitionOutput {
1485
1574
  suggestedRelations?: WorkspaceYaml['suggestedRelations'];
1486
1575
  displayTemplates?: WorkspaceYaml['displayTemplates'];
1487
1576
  entityLinks?: WorkspaceYaml['entityLinks'];
1577
+ /** Relation-type vocabulary — carried verbatim (authored form IS the wire form). */
1578
+ relationDefs?: WorkspaceYaml['relationDefs'];
1579
+ /** Intelligence commands — carried verbatim (authored form IS the wire form). */
1580
+ commands?: WorkspaceYaml['commands'];
1488
1581
  /** WIDGET half of the authored bento union — mapped by the pod via `widgetType`. */
1489
1582
  bentoLayout?: BentoWidgetBlockWire[];
1490
1583
  /**