@proveanything/smartlinks 2.1.3 → 2.1.4

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.
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.1.3 | Generated: 2026-10-26T21:00:54.277Z
3
+ Version: 2.1.4 | Generated: 2026-10-28T12:35:09.709Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -2713,6 +2713,7 @@ interface AppFunctionAgentExposure {
2713
2713
  description?: string;
2714
2714
  input?: Record<string, any>;
2715
2715
  approval?: 'auto' | 'require';
2716
+ audience?: Array<'app' | 'public' | 'assistant'>;
2716
2717
  }
2717
2718
  ```
2718
2719
 
@@ -3051,10 +3052,29 @@ interface AppManifest {
3051
3052
  data?: AppDataDeclaration;
3052
3053
  headless?: AppHeadlessDeclaration;
3053
3054
  connector?: AppConnectorDeclaration;
3055
+ assistant?: AppAssistantDeclaration;
3054
3056
  [key: string]: any;
3055
3057
  }
3056
3058
  ```
3057
3059
 
3060
+ **AppAssistantFile** (interface)
3061
+ ```typescript
3062
+ interface AppAssistantFile {
3063
+ name: string;
3064
+ description?: string;
3065
+ file: string;
3066
+ }
3067
+ ```
3068
+
3069
+ **AppAssistantDeclaration** (interface)
3070
+ ```typescript
3071
+ interface AppAssistantDeclaration {
3072
+ skills?: AppAssistantFile[];
3073
+ knowledge?: AppAssistantFile[];
3074
+ preview?: string;
3075
+ }
3076
+ ```
3077
+
3058
3078
  **ConnectorConnectionField** (interface)
3059
3079
  ```typescript
3060
3080
  interface ConnectorConnectionField {
@@ -44,7 +44,8 @@ needs to call it — a description and an input schema:
44
44
  "title": "Create an FAQ entry",
45
45
  "description": "Add a question/answer to this collection's FAQ. Use when the user asks to add or draft an FAQ.",
46
46
  "input": { /* JSON Schema for the arguments the agent supplies as event.body */ },
47
- "approval": "auto" // auto | require (require = human confirmation before the call)
47
+ "approval": "auto", // auto | require (require = human confirmation before the call)
48
+ "audience": ["app"] // optional: app | public | assistant — where it is offered (absent = everywhere)
48
49
  }
49
50
  }
50
51
  ]
@@ -103,6 +104,10 @@ Identical to server functions — nothing new to reason about:
103
104
  - **`visibility`** — who may call (admin/public).
104
105
  - **`authority`** — whose identity `ctx.sl` carries (`caller` vs `collection`).
105
106
  - **`capabilities`** — least-privilege, capped even when elevated.
107
+ - **`agent.audience`** — where the tool is offered: `'app'` (the app's own agent runs, admin surface),
108
+ `'public'` (public assistants), `'assistant'` (the Studio assistant, the admin's agent —
109
+ [assistant-apps.md](assistant-apps.md)). Absent = everywhere the function's visibility allows. An
110
+ assistant-only tool declares `["assistant"]` and never joins an in-app or public run.
106
111
  - **`agent.approval: "require"`** — the agent must get human confirmation before invoking (use for
107
112
  destructive or public-facing actions).
108
113
 
@@ -460,6 +460,30 @@ lists it and draws its connect form and settings, and the integration engine cal
460
460
  functions (`testConnection`, `describeTarget`, `sample`, `fetch`, `shape`, `serialize`, `send`). It adapts the provider's format; core does each customer's mapping. The full
461
461
  contract is in the **[Connectors guide](connectors.md)**.
462
462
 
463
+ #### `assistant`
464
+
465
+ What the **Studio assistant** (the admin's own agent) knows about this app: skills and knowledge files
466
+ shipped with the build and loaded into its box on demand, and an optional page that renders a planned
467
+ change set before it is applied.
468
+
469
+ ```jsonc
470
+ "assistant": {
471
+ "skills": [
472
+ { "name": "theme", "description": "Changing the Hub's colours, fonts or logo", "file": "assistant/skills/theme.md" },
473
+ { "name": "pages", "description": "Adding, ordering and editing pages and the menu", "file": "assistant/skills/pages.md" }
474
+ ],
475
+ "knowledge": [
476
+ { "name": "settings-schema", "description": "JSON Schema of the hub settings group", "file": "assistant/knowledge/settings.schema.json" }
477
+ ],
478
+ "preview": "/admin/preview?changeSet={id}"
479
+ }
480
+ ```
481
+
482
+ The files live under `public/assistant/` and ship with every release like any other static file. The
483
+ assistant sees one line per skill (its description) and reads the file only when the job calls for it,
484
+ so a big app can carry many skills at no cost to the conversation. Full guide, including what a skill
485
+ should say and how the assistant changes config with undo: **[assistant-apps.md](assistant-apps.md)**.
486
+
463
487
  ---
464
488
 
465
489
  ## `app.admin.json`
@@ -0,0 +1,168 @@
1
+ # The Studio assistant and your app
2
+
3
+ SmartLinks admins have **one assistant**: an agent that works for the signed-in admin on one
4
+ collection, with the admin's own authority, from a chat in Studio. It reads and changes data through
5
+ the SmartLinks API, every write is a **change set** the admin can see and undo, and the things that
6
+ can't be undone (deleting, sending, calling out, publishing, spending) wait for the admin's "Allow".
7
+
8
+ This page is for app authors: how the assistant learns what your app is, how it is set up, and what it
9
+ can do for the admin — without you building a chat of your own. Your app declares three things:
10
+
11
+ | What | Where | Status |
12
+ |---|---|---|
13
+ | **Skills and knowledge** — plain-words procedures for the admin side, and the facts they need | `assistant` block in `app.manifest.json`, files under `public/assistant/` | this page |
14
+ | **Tools** — logic the assistant should call rather than reimplement | server functions with an `agent` block (`audience: ["assistant"]` for assistant-only) | [agent-tools.md](agent-tools.md) |
15
+ | **Setup** — questions, config schema, how to save | `app.admin.json` + `ai-guide.md` | [app-manifest.md](app-manifest.md#appadminjson) |
16
+
17
+ An app with none of these still works with the assistant: it can read the app's records and config
18
+ and change them through change sets. The declarations make it *good* at your app.
19
+
20
+ ## The `assistant` block
21
+
22
+ ```jsonc
23
+ // app.manifest.json
24
+ "assistant": {
25
+ "skills": [
26
+ { "name": "setup", "description": "First-time setup of the FAQ app: categories, tone, who answers", "file": "assistant/skills/setup.md" },
27
+ { "name": "content", "description": "Adding, editing, ordering and retiring questions and answers", "file": "assistant/skills/content.md" }
28
+ ],
29
+ "knowledge": [
30
+ { "name": "config-schema", "description": "JSON Schema of the app's config document", "file": "assistant/knowledge/config.schema.json" },
31
+ { "name": "examples", "description": "Three worked examples of good FAQ content", "file": "assistant/knowledge/examples.md" }
32
+ ],
33
+ "preview": "/admin/preview?changeSet={id}"
34
+ }
35
+ ```
36
+
37
+ The files sit in `public/assistant/` in your project and ship with every release like any other static
38
+ file (they are read from the release's CDN folder, on the channel the collection runs your app on).
39
+
40
+ ### How they are loaded
41
+
42
+ When an admin opens a chat, the assistant's box gets the **index** of every installed app's skills: one
43
+ line each, the description. Nothing else. A skill's body, and the knowledge files it names, are read
44
+ only when the assistant decides the job needs them. Twenty apps with three skills each cost the
45
+ conversation about sixty lines. So: write several focused skills rather than one long one, and spend
46
+ care on the description — it is how the assistant decides to open the file.
47
+
48
+ ### Writing a skill
49
+
50
+ A skill is a markdown file: what this area of your app is for, what each setting means, how to read and
51
+ change it, what a good result looks like, what to check afterwards. Write it for a capable colleague who
52
+ has never seen your app. The assistant works through the SmartLinks API with a small CLI (`sl`), so say
53
+ things in those terms:
54
+
55
+ ```markdown
56
+ ---
57
+ name: faq-content
58
+ description: Adding, editing, ordering and retiring questions and answers in the FAQ app
59
+ ---
60
+ # FAQ content
61
+
62
+ Questions live as app records of type `question` (`sl get app/faq/records recordType=question`).
63
+ Fields: `data.question`, `data.answer` (markdown), `data.category` (one of the categories in the app's
64
+ config, `sl get app/faq` → `categories[]`), `data.order` (ascending), `status` (`published` | `draft`).
65
+
66
+ ## Adding questions
67
+ Plan one change set per batch: items `{ "op": "create", "entity": "record", "appId": "faq",
68
+ "data": { "recordType": "question", "data": { … }, "status": "draft" } }`. Match on `data.question`
69
+ (case-insensitive) before creating, so a re-run updates instead of duplicating.
70
+
71
+ ## A good answer
72
+ Two to five sentences, plain words, no marketing. Link to a product page when one exists
73
+ (`/p/<collectionId>/<productId>`). Never invent policy: if the answer depends on something the admin
74
+ hasn't said (returns window, delivery areas), ask.
75
+
76
+ ## Check afterwards
77
+ `sl get app/faq/records recordType=question status=draft` — read three back and make sure the
78
+ category is one that exists.
79
+ ```
80
+
81
+ The frontmatter is optional; when it is missing, the manifest entry's `name` and `description` are used.
82
+ Keep a skill under a few hundred lines; put long reference material in knowledge files and name them.
83
+
84
+ ### Knowledge
85
+
86
+ Knowledge files are what the skills refer to: the schema of your config document, record shapes, worked
87
+ examples, a glossary of your app's words. The assistant reads them when a skill points at them (each
88
+ skill is told where the app's knowledge files are).
89
+
90
+ **Generate the schema, don't write it.** If your config is typed (a TypeScript interface, a zod schema),
91
+ produce the JSON Schema at build time (`ts-json-schema-generator`, `zod-to-json-schema`) into
92
+ `public/assistant/knowledge/`. A hand-written schema drifts the first week.
93
+
94
+ ## Tools: when a skill is not enough
95
+
96
+ Nearly every admin action is a read or a patch of config or records, and the assistant does those
97
+ itself. Give it a **tool** only when a step can't be an API call it makes on its own:
98
+
99
+ - logic that lives in your code (validate a layout, render a page from a design direction, compute
100
+ something from your data model),
101
+ - a write that must be atomic across several objects,
102
+ - anything that needs your app's secrets.
103
+
104
+ A tool is an http server function with an `agent` block
105
+ ([agent-tools.md](agent-tools.md)); the assistant calls it as
106
+ `sl post app/<appId>/functions/<name> '{ … }'`, under the function's own visibility, authority and
107
+ capabilities. Set `"audience": ["assistant"]` to keep an assistant-only tool out of your in-app and
108
+ public agent runs:
109
+
110
+ ```jsonc
111
+ {
112
+ "name": "applyDesignDirection",
113
+ "trigger": { "type": "http" },
114
+ "visibility": "admin",
115
+ "authority": "caller",
116
+ "capabilities": ["sl:settings:write"],
117
+ "agent": {
118
+ "tool": true,
119
+ "title": "Apply a design direction",
120
+ "description": "Rewrites the theme, fonts and layout of the hub to match a named direction.",
121
+ "input": { "type": "object", "properties": { "direction": { "type": "string" } }, "required": ["direction"] },
122
+ "audience": ["assistant"]
123
+ }
124
+ }
125
+ ```
126
+
127
+ A tool that changes config should **return the patch, not apply it**, so the assistant can put it in a
128
+ change set and the admin keeps undo. Tools that act directly are fine for reads and computations.
129
+
130
+ ## How the assistant changes your config
131
+
132
+ Your config document (`SL.appConfiguration`, `sl get app/<appId>`) and the collection's settings
133
+ groups (`sl get settings/<group>`) are changed through change-set items:
134
+
135
+ ```jsonc
136
+ { "op": "update", "entity": "appConfig", "id": "<appId>", "data": { "theme": { "accent": "#0a7" } } }
137
+ { "op": "update", "entity": "appConfig", "id": "<appId>", "scope": { "productId": "p1" }, "data": { … } }
138
+ { "op": "update", "entity": "settings", "id": "hub", "data": { "pages": { "about": { … } } } }
139
+ ```
140
+
141
+ Saves deep-merge, as your own admin UI's saves do (nested objects merge, arrays replace). The whole
142
+ document before the change is kept, so **undo puts it back exactly**. Say in your skills which keys
143
+ are arrays, so the assistant sends the whole array when it changes one element.
144
+
145
+ ### Preview before apply
146
+
147
+ A planned change set can be rendered before it is applied. The API gives, for every document a set
148
+ touches, the live document and the document as it would become:
149
+
150
+ ```
151
+ GET /admin/collection/:collectionId/assistant/changesets/:id/preview
152
+ → { id, status, targets: [ { entity, id, scope?, before, after, ops } ] }
153
+ ```
154
+
155
+ Declare `assistant.preview` — a page of your admin surface that takes `{id}`, calls that endpoint
156
+ with the admin's session, and renders `targets[].after` as your app would render the live config. The
157
+ Studio panel then shows "Preview" on the change set, and the admin sees the result before saying yes.
158
+
159
+ ## Checklist
160
+
161
+ - `assistant.skills[]`: several focused skills with descriptions that say when they apply.
162
+ - `assistant.knowledge[]`: the config schema generated from your types; record shapes; examples.
163
+ - Every skill says which fields are arrays, what to match on before creating, what to check after.
164
+ - Logic the assistant can't reproduce: a server function with `agent.tool` and `audience: ["assistant"]`,
165
+ returning a patch for config changes.
166
+ - `assistant.preview` when your app has a visual surface worth seeing before a change lands.
167
+ - `ai-guide.md` still describes first-time setup for the setup wizard; the assistant reads it too when
168
+ an app declares no skills.
package/dist/openapi.yaml CHANGED
@@ -20138,6 +20138,14 @@ components:
20138
20138
  enum:
20139
20139
  - auto
20140
20140
  - require
20141
+ audience:
20142
+ type: array
20143
+ items:
20144
+ type: string
20145
+ enum:
20146
+ - app
20147
+ - public
20148
+ - assistant
20141
20149
  required:
20142
20150
  - tool
20143
20151
  AppManifestFunctions:
@@ -20635,6 +20643,8 @@ components:
20635
20643
  $ref: "#/components/schemas/AppHeadlessDeclaration"
20636
20644
  connector:
20637
20645
  $ref: "#/components/schemas/AppConnectorDeclaration"
20646
+ assistant:
20647
+ $ref: "#/components/schemas/AppAssistantDeclaration"
20638
20648
  required:
20639
20649
  - name
20640
20650
  - version
@@ -20642,6 +20652,31 @@ components:
20642
20652
  - function
20643
20653
  - files
20644
20654
  - components
20655
+ AppAssistantFile:
20656
+ type: object
20657
+ properties:
20658
+ name:
20659
+ type: string
20660
+ description:
20661
+ type: string
20662
+ file:
20663
+ type: string
20664
+ required:
20665
+ - name
20666
+ - file
20667
+ AppAssistantDeclaration:
20668
+ type: object
20669
+ properties:
20670
+ skills:
20671
+ type: array
20672
+ items:
20673
+ $ref: "#/components/schemas/AppAssistantFile"
20674
+ knowledge:
20675
+ type: array
20676
+ items:
20677
+ $ref: "#/components/schemas/AppAssistantFile"
20678
+ preview:
20679
+ type: string
20645
20680
  ConnectorConnectionField:
20646
20681
  type: object
20647
20682
  properties:
@@ -349,6 +349,13 @@ export interface AppFunctionAgentExposure {
349
349
  * the autonomous server-side loop (they'd otherwise run unconfirmed).
350
350
  */
351
351
  approval?: 'auto' | 'require';
352
+ /**
353
+ * Where the tool is offered. `'app'` = the app's own agent runs (admin surface), `'public'` = public
354
+ * assistants, `'assistant'` = the Studio assistant (the admin's agent, docs/assistant-apps.md).
355
+ * Absent = everywhere the function's visibility allows. An assistant-only tool (logic the assistant
356
+ * needs that lives in your code) declares `['assistant']` and never shows in an in-app or public run.
357
+ */
358
+ audience?: Array<'app' | 'public' | 'assistant'>;
352
359
  }
353
360
  /** The `functions` block in `app.manifest.json`. Presence means the app ships server functions. */
354
361
  export interface AppManifestFunctions {
@@ -941,8 +948,52 @@ export interface AppManifest {
941
948
  * @see AppConnectorDeclaration · docs/connectors.md
942
949
  */
943
950
  connector?: AppConnectorDeclaration;
951
+ /**
952
+ * What the Studio assistant (the admin's agent) knows about this app: skills and knowledge files
953
+ * shipped with the build and loaded into its box on demand, and an optional page that renders a
954
+ * planned change set. See docs/assistant-apps.md. @see AppAssistantDeclaration
955
+ */
956
+ assistant?: AppAssistantDeclaration;
944
957
  [key: string]: any;
945
958
  }
959
+ /** One skill or knowledge file the app ships for the assistant. */
960
+ export interface AppAssistantFile {
961
+ /** Short id, unique within the app (`theme`, `pages`, `settings-schema`). */
962
+ name: string;
963
+ /**
964
+ * One line: what it covers and when to reach for it. For a skill this is what the assistant sees
965
+ * before deciding to read the file, so say when it applies ("Changing the Hub's colours, fonts or
966
+ * logo"). A knowledge file's description says what is in it.
967
+ */
968
+ description?: string;
969
+ /**
970
+ * Path of the file in the published build, relative to the app's root (`public/` in the project):
971
+ * `assistant/skills/theme.md`. Markdown for skills; markdown or JSON (a JSON Schema) for knowledge.
972
+ */
973
+ file: string;
974
+ }
975
+ /** The `assistant` block in `app.manifest.json`. */
976
+ export interface AppAssistantDeclaration {
977
+ /**
978
+ * Skills: plain-words procedures for one area of the app's admin side — what the settings mean,
979
+ * which calls read and change them, what a good result looks like, what to check. Several for a big
980
+ * app (identity and theme; pages and navigation; domains and SEO). A skill file may start with
981
+ * YAML frontmatter (`name`, `description`); if it doesn't, these entries supply it.
982
+ */
983
+ skills?: AppAssistantFile[];
984
+ /**
985
+ * Knowledge the skills refer to: the settings schema (generate it from your config types — never
986
+ * by hand), object definitions, worked examples, a glossary. Loaded only when a skill reaches for it.
987
+ */
988
+ knowledge?: AppAssistantFile[];
989
+ /**
990
+ * A page of the app that renders a PLANNED change set over the live config, so the admin can see a
991
+ * change before it is applied. `{id}` is replaced with the change-set id; the page reads
992
+ * `GET /admin/collection/:cid/assistant/changesets/:id/preview` and renders `targets[].after`.
993
+ * Relative to the app's admin surface, e.g. `"/admin/preview?changeSet={id}"`.
994
+ */
995
+ preview?: string;
996
+ }
946
997
  /** One field of a connector's "Connect" form. */
947
998
  export interface ConnectorConnectionField {
948
999
  /** Key in the installation's connection settings (non-secret fields) — `name` for a secret too. */
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.1.3 | Generated: 2026-10-26T21:00:54.277Z
3
+ Version: 2.1.4 | Generated: 2026-10-28T12:35:09.709Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -2713,6 +2713,7 @@ interface AppFunctionAgentExposure {
2713
2713
  description?: string;
2714
2714
  input?: Record<string, any>;
2715
2715
  approval?: 'auto' | 'require';
2716
+ audience?: Array<'app' | 'public' | 'assistant'>;
2716
2717
  }
2717
2718
  ```
2718
2719
 
@@ -3051,10 +3052,29 @@ interface AppManifest {
3051
3052
  data?: AppDataDeclaration;
3052
3053
  headless?: AppHeadlessDeclaration;
3053
3054
  connector?: AppConnectorDeclaration;
3055
+ assistant?: AppAssistantDeclaration;
3054
3056
  [key: string]: any;
3055
3057
  }
3056
3058
  ```
3057
3059
 
3060
+ **AppAssistantFile** (interface)
3061
+ ```typescript
3062
+ interface AppAssistantFile {
3063
+ name: string;
3064
+ description?: string;
3065
+ file: string;
3066
+ }
3067
+ ```
3068
+
3069
+ **AppAssistantDeclaration** (interface)
3070
+ ```typescript
3071
+ interface AppAssistantDeclaration {
3072
+ skills?: AppAssistantFile[];
3073
+ knowledge?: AppAssistantFile[];
3074
+ preview?: string;
3075
+ }
3076
+ ```
3077
+
3058
3078
  **ConnectorConnectionField** (interface)
3059
3079
  ```typescript
3060
3080
  interface ConnectorConnectionField {
@@ -44,7 +44,8 @@ needs to call it — a description and an input schema:
44
44
  "title": "Create an FAQ entry",
45
45
  "description": "Add a question/answer to this collection's FAQ. Use when the user asks to add or draft an FAQ.",
46
46
  "input": { /* JSON Schema for the arguments the agent supplies as event.body */ },
47
- "approval": "auto" // auto | require (require = human confirmation before the call)
47
+ "approval": "auto", // auto | require (require = human confirmation before the call)
48
+ "audience": ["app"] // optional: app | public | assistant — where it is offered (absent = everywhere)
48
49
  }
49
50
  }
50
51
  ]
@@ -103,6 +104,10 @@ Identical to server functions — nothing new to reason about:
103
104
  - **`visibility`** — who may call (admin/public).
104
105
  - **`authority`** — whose identity `ctx.sl` carries (`caller` vs `collection`).
105
106
  - **`capabilities`** — least-privilege, capped even when elevated.
107
+ - **`agent.audience`** — where the tool is offered: `'app'` (the app's own agent runs, admin surface),
108
+ `'public'` (public assistants), `'assistant'` (the Studio assistant, the admin's agent —
109
+ [assistant-apps.md](assistant-apps.md)). Absent = everywhere the function's visibility allows. An
110
+ assistant-only tool declares `["assistant"]` and never joins an in-app or public run.
106
111
  - **`agent.approval: "require"`** — the agent must get human confirmation before invoking (use for
107
112
  destructive or public-facing actions).
108
113
 
@@ -460,6 +460,30 @@ lists it and draws its connect form and settings, and the integration engine cal
460
460
  functions (`testConnection`, `describeTarget`, `sample`, `fetch`, `shape`, `serialize`, `send`). It adapts the provider's format; core does each customer's mapping. The full
461
461
  contract is in the **[Connectors guide](connectors.md)**.
462
462
 
463
+ #### `assistant`
464
+
465
+ What the **Studio assistant** (the admin's own agent) knows about this app: skills and knowledge files
466
+ shipped with the build and loaded into its box on demand, and an optional page that renders a planned
467
+ change set before it is applied.
468
+
469
+ ```jsonc
470
+ "assistant": {
471
+ "skills": [
472
+ { "name": "theme", "description": "Changing the Hub's colours, fonts or logo", "file": "assistant/skills/theme.md" },
473
+ { "name": "pages", "description": "Adding, ordering and editing pages and the menu", "file": "assistant/skills/pages.md" }
474
+ ],
475
+ "knowledge": [
476
+ { "name": "settings-schema", "description": "JSON Schema of the hub settings group", "file": "assistant/knowledge/settings.schema.json" }
477
+ ],
478
+ "preview": "/admin/preview?changeSet={id}"
479
+ }
480
+ ```
481
+
482
+ The files live under `public/assistant/` and ship with every release like any other static file. The
483
+ assistant sees one line per skill (its description) and reads the file only when the job calls for it,
484
+ so a big app can carry many skills at no cost to the conversation. Full guide, including what a skill
485
+ should say and how the assistant changes config with undo: **[assistant-apps.md](assistant-apps.md)**.
486
+
463
487
  ---
464
488
 
465
489
  ## `app.admin.json`
@@ -0,0 +1,168 @@
1
+ # The Studio assistant and your app
2
+
3
+ SmartLinks admins have **one assistant**: an agent that works for the signed-in admin on one
4
+ collection, with the admin's own authority, from a chat in Studio. It reads and changes data through
5
+ the SmartLinks API, every write is a **change set** the admin can see and undo, and the things that
6
+ can't be undone (deleting, sending, calling out, publishing, spending) wait for the admin's "Allow".
7
+
8
+ This page is for app authors: how the assistant learns what your app is, how it is set up, and what it
9
+ can do for the admin — without you building a chat of your own. Your app declares three things:
10
+
11
+ | What | Where | Status |
12
+ |---|---|---|
13
+ | **Skills and knowledge** — plain-words procedures for the admin side, and the facts they need | `assistant` block in `app.manifest.json`, files under `public/assistant/` | this page |
14
+ | **Tools** — logic the assistant should call rather than reimplement | server functions with an `agent` block (`audience: ["assistant"]` for assistant-only) | [agent-tools.md](agent-tools.md) |
15
+ | **Setup** — questions, config schema, how to save | `app.admin.json` + `ai-guide.md` | [app-manifest.md](app-manifest.md#appadminjson) |
16
+
17
+ An app with none of these still works with the assistant: it can read the app's records and config
18
+ and change them through change sets. The declarations make it *good* at your app.
19
+
20
+ ## The `assistant` block
21
+
22
+ ```jsonc
23
+ // app.manifest.json
24
+ "assistant": {
25
+ "skills": [
26
+ { "name": "setup", "description": "First-time setup of the FAQ app: categories, tone, who answers", "file": "assistant/skills/setup.md" },
27
+ { "name": "content", "description": "Adding, editing, ordering and retiring questions and answers", "file": "assistant/skills/content.md" }
28
+ ],
29
+ "knowledge": [
30
+ { "name": "config-schema", "description": "JSON Schema of the app's config document", "file": "assistant/knowledge/config.schema.json" },
31
+ { "name": "examples", "description": "Three worked examples of good FAQ content", "file": "assistant/knowledge/examples.md" }
32
+ ],
33
+ "preview": "/admin/preview?changeSet={id}"
34
+ }
35
+ ```
36
+
37
+ The files sit in `public/assistant/` in your project and ship with every release like any other static
38
+ file (they are read from the release's CDN folder, on the channel the collection runs your app on).
39
+
40
+ ### How they are loaded
41
+
42
+ When an admin opens a chat, the assistant's box gets the **index** of every installed app's skills: one
43
+ line each, the description. Nothing else. A skill's body, and the knowledge files it names, are read
44
+ only when the assistant decides the job needs them. Twenty apps with three skills each cost the
45
+ conversation about sixty lines. So: write several focused skills rather than one long one, and spend
46
+ care on the description — it is how the assistant decides to open the file.
47
+
48
+ ### Writing a skill
49
+
50
+ A skill is a markdown file: what this area of your app is for, what each setting means, how to read and
51
+ change it, what a good result looks like, what to check afterwards. Write it for a capable colleague who
52
+ has never seen your app. The assistant works through the SmartLinks API with a small CLI (`sl`), so say
53
+ things in those terms:
54
+
55
+ ```markdown
56
+ ---
57
+ name: faq-content
58
+ description: Adding, editing, ordering and retiring questions and answers in the FAQ app
59
+ ---
60
+ # FAQ content
61
+
62
+ Questions live as app records of type `question` (`sl get app/faq/records recordType=question`).
63
+ Fields: `data.question`, `data.answer` (markdown), `data.category` (one of the categories in the app's
64
+ config, `sl get app/faq` → `categories[]`), `data.order` (ascending), `status` (`published` | `draft`).
65
+
66
+ ## Adding questions
67
+ Plan one change set per batch: items `{ "op": "create", "entity": "record", "appId": "faq",
68
+ "data": { "recordType": "question", "data": { … }, "status": "draft" } }`. Match on `data.question`
69
+ (case-insensitive) before creating, so a re-run updates instead of duplicating.
70
+
71
+ ## A good answer
72
+ Two to five sentences, plain words, no marketing. Link to a product page when one exists
73
+ (`/p/<collectionId>/<productId>`). Never invent policy: if the answer depends on something the admin
74
+ hasn't said (returns window, delivery areas), ask.
75
+
76
+ ## Check afterwards
77
+ `sl get app/faq/records recordType=question status=draft` — read three back and make sure the
78
+ category is one that exists.
79
+ ```
80
+
81
+ The frontmatter is optional; when it is missing, the manifest entry's `name` and `description` are used.
82
+ Keep a skill under a few hundred lines; put long reference material in knowledge files and name them.
83
+
84
+ ### Knowledge
85
+
86
+ Knowledge files are what the skills refer to: the schema of your config document, record shapes, worked
87
+ examples, a glossary of your app's words. The assistant reads them when a skill points at them (each
88
+ skill is told where the app's knowledge files are).
89
+
90
+ **Generate the schema, don't write it.** If your config is typed (a TypeScript interface, a zod schema),
91
+ produce the JSON Schema at build time (`ts-json-schema-generator`, `zod-to-json-schema`) into
92
+ `public/assistant/knowledge/`. A hand-written schema drifts the first week.
93
+
94
+ ## Tools: when a skill is not enough
95
+
96
+ Nearly every admin action is a read or a patch of config or records, and the assistant does those
97
+ itself. Give it a **tool** only when a step can't be an API call it makes on its own:
98
+
99
+ - logic that lives in your code (validate a layout, render a page from a design direction, compute
100
+ something from your data model),
101
+ - a write that must be atomic across several objects,
102
+ - anything that needs your app's secrets.
103
+
104
+ A tool is an http server function with an `agent` block
105
+ ([agent-tools.md](agent-tools.md)); the assistant calls it as
106
+ `sl post app/<appId>/functions/<name> '{ … }'`, under the function's own visibility, authority and
107
+ capabilities. Set `"audience": ["assistant"]` to keep an assistant-only tool out of your in-app and
108
+ public agent runs:
109
+
110
+ ```jsonc
111
+ {
112
+ "name": "applyDesignDirection",
113
+ "trigger": { "type": "http" },
114
+ "visibility": "admin",
115
+ "authority": "caller",
116
+ "capabilities": ["sl:settings:write"],
117
+ "agent": {
118
+ "tool": true,
119
+ "title": "Apply a design direction",
120
+ "description": "Rewrites the theme, fonts and layout of the hub to match a named direction.",
121
+ "input": { "type": "object", "properties": { "direction": { "type": "string" } }, "required": ["direction"] },
122
+ "audience": ["assistant"]
123
+ }
124
+ }
125
+ ```
126
+
127
+ A tool that changes config should **return the patch, not apply it**, so the assistant can put it in a
128
+ change set and the admin keeps undo. Tools that act directly are fine for reads and computations.
129
+
130
+ ## How the assistant changes your config
131
+
132
+ Your config document (`SL.appConfiguration`, `sl get app/<appId>`) and the collection's settings
133
+ groups (`sl get settings/<group>`) are changed through change-set items:
134
+
135
+ ```jsonc
136
+ { "op": "update", "entity": "appConfig", "id": "<appId>", "data": { "theme": { "accent": "#0a7" } } }
137
+ { "op": "update", "entity": "appConfig", "id": "<appId>", "scope": { "productId": "p1" }, "data": { … } }
138
+ { "op": "update", "entity": "settings", "id": "hub", "data": { "pages": { "about": { … } } } }
139
+ ```
140
+
141
+ Saves deep-merge, as your own admin UI's saves do (nested objects merge, arrays replace). The whole
142
+ document before the change is kept, so **undo puts it back exactly**. Say in your skills which keys
143
+ are arrays, so the assistant sends the whole array when it changes one element.
144
+
145
+ ### Preview before apply
146
+
147
+ A planned change set can be rendered before it is applied. The API gives, for every document a set
148
+ touches, the live document and the document as it would become:
149
+
150
+ ```
151
+ GET /admin/collection/:collectionId/assistant/changesets/:id/preview
152
+ → { id, status, targets: [ { entity, id, scope?, before, after, ops } ] }
153
+ ```
154
+
155
+ Declare `assistant.preview` — a page of your admin surface that takes `{id}`, calls that endpoint
156
+ with the admin's session, and renders `targets[].after` as your app would render the live config. The
157
+ Studio panel then shows "Preview" on the change set, and the admin sees the result before saying yes.
158
+
159
+ ## Checklist
160
+
161
+ - `assistant.skills[]`: several focused skills with descriptions that say when they apply.
162
+ - `assistant.knowledge[]`: the config schema generated from your types; record shapes; examples.
163
+ - Every skill says which fields are arrays, what to match on before creating, what to check after.
164
+ - Logic the assistant can't reproduce: a server function with `agent.tool` and `audience: ["assistant"]`,
165
+ returning a patch for config changes.
166
+ - `assistant.preview` when your app has a visual surface worth seeing before a change lands.
167
+ - `ai-guide.md` still describes first-time setup for the setup wizard; the assistant reads it too when
168
+ an app declares no skills.
package/openapi.yaml CHANGED
@@ -20138,6 +20138,14 @@ components:
20138
20138
  enum:
20139
20139
  - auto
20140
20140
  - require
20141
+ audience:
20142
+ type: array
20143
+ items:
20144
+ type: string
20145
+ enum:
20146
+ - app
20147
+ - public
20148
+ - assistant
20141
20149
  required:
20142
20150
  - tool
20143
20151
  AppManifestFunctions:
@@ -20635,6 +20643,8 @@ components:
20635
20643
  $ref: "#/components/schemas/AppHeadlessDeclaration"
20636
20644
  connector:
20637
20645
  $ref: "#/components/schemas/AppConnectorDeclaration"
20646
+ assistant:
20647
+ $ref: "#/components/schemas/AppAssistantDeclaration"
20638
20648
  required:
20639
20649
  - name
20640
20650
  - version
@@ -20642,6 +20652,31 @@ components:
20642
20652
  - function
20643
20653
  - files
20644
20654
  - components
20655
+ AppAssistantFile:
20656
+ type: object
20657
+ properties:
20658
+ name:
20659
+ type: string
20660
+ description:
20661
+ type: string
20662
+ file:
20663
+ type: string
20664
+ required:
20665
+ - name
20666
+ - file
20667
+ AppAssistantDeclaration:
20668
+ type: object
20669
+ properties:
20670
+ skills:
20671
+ type: array
20672
+ items:
20673
+ $ref: "#/components/schemas/AppAssistantFile"
20674
+ knowledge:
20675
+ type: array
20676
+ items:
20677
+ $ref: "#/components/schemas/AppAssistantFile"
20678
+ preview:
20679
+ type: string
20645
20680
  ConnectorConnectionField:
20646
20681
  type: object
20647
20682
  properties:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",