@proveanything/smartlinks 2.0.5 → 2.0.6
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/docs/API_SUMMARY.md +1 -1
- package/dist/docs/agent-tools.md +111 -0
- package/dist/docs/deploying-apps.md +8 -3
- package/dist/docs/host-dependency-contract.md +132 -0
- package/dist/docs/overview.md +3 -1
- package/dist/docs/sequences.md +1 -1
- package/dist/docs/server-functions.md +2 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/API_SUMMARY.md +1 -1
- package/docs/agent-tools.md +111 -0
- package/docs/deploying-apps.md +8 -3
- package/docs/host-dependency-contract.md +132 -0
- package/docs/overview.md +3 -1
- package/docs/sequences.md +1 -1
- package/docs/server-functions.md +2 -3
- package/package.json +1 -1
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Agent tools — exposing your app's actions to the SmartLinks agent
|
|
2
|
+
|
|
3
|
+
> **SmartLinks SDK 2.x.** Redraft of the earlier "Agent Skills & Tools" RFC, now built on
|
|
4
|
+
> [server functions](server-functions.md). **Staged rollout:** the *declaration + discovery* ship in
|
|
5
|
+
> V2 (do them now); the *live agent loop* (a parent chat agent calling your tools) lights up when the
|
|
6
|
+
> platform agent can consume them. Everything you declare is useful before then — a tool is a server
|
|
7
|
+
> function, already invocable via http/event.
|
|
8
|
+
|
|
9
|
+
## The model: app-authored capabilities
|
|
10
|
+
|
|
11
|
+
An app declares **capabilities** — app-authored logic the platform runs, each with a declared
|
|
12
|
+
security envelope (`visibility` / `authority` / `capabilities`). There is **one engine** (the
|
|
13
|
+
server-functions runtime) and several **triggers** into it: `http`, `event`, `cron`, and **`agent`**.
|
|
14
|
+
|
|
15
|
+
**An agent tool is not a new runtime — it's an MCP-shaped facade over a capability.** By default a
|
|
16
|
+
tool *is a server function*: the agent's `tools/call` routes into the same `(ctx, event) => result`
|
|
17
|
+
with the same capability envelope. You author the logic once; the agent is just another caller.
|
|
18
|
+
|
|
19
|
+
Use a **client tool** (a `postMessage` handler in your live admin iframe) only for genuinely
|
|
20
|
+
UI-coupled actions that must run in the open app. It is **not** the default — headless server
|
|
21
|
+
functions are, because they work whether or not your UI is mounted.
|
|
22
|
+
|
|
23
|
+
## Declaring a tool
|
|
24
|
+
|
|
25
|
+
You already declare server functions in `app.manifest.json` (see
|
|
26
|
+
[server-functions.md](server-functions.md)). Mark one **agent-invocable** and give the agent what it
|
|
27
|
+
needs to call it — a description and an input schema:
|
|
28
|
+
|
|
29
|
+
```jsonc
|
|
30
|
+
{
|
|
31
|
+
"functions": {
|
|
32
|
+
"files": { "js": { "umd": "dist/functions.umd.js" } },
|
|
33
|
+
"definitions": [
|
|
34
|
+
{
|
|
35
|
+
"name": "createFaq",
|
|
36
|
+
"trigger": { "type": "http", "methods": ["POST"] },
|
|
37
|
+
"visibility": "admin",
|
|
38
|
+
"authority": "collection",
|
|
39
|
+
"capabilities": ["sl:records:write"],
|
|
40
|
+
|
|
41
|
+
// ── makes it an agent tool ──
|
|
42
|
+
"agent": {
|
|
43
|
+
"tool": true,
|
|
44
|
+
"title": "Create an FAQ entry",
|
|
45
|
+
"description": "Add a question/answer to this collection's FAQ. Use when the user asks to add or draft an FAQ.",
|
|
46
|
+
"input": { /* JSON Schema for the arguments the agent supplies as event.body */ },
|
|
47
|
+
"approval": "auto" // auto | require (require = human confirmation before the call)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The handler is an ordinary server function — the agent-supplied arguments arrive as `event.body`:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
export async function createFaq(ctx, event) {
|
|
59
|
+
const { question, answer } = event.body || {}
|
|
60
|
+
// validate, then act with your declared authority/capabilities:
|
|
61
|
+
const rec = await ctx.sl.appRecords.create({ recordType: 'faq', data: { question, answer } })
|
|
62
|
+
return { id: rec.id }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## What the agent sees (MCP shape)
|
|
67
|
+
|
|
68
|
+
The platform derives an **MCP tool list** from your manifest and drives the standard flow — `tools/list`
|
|
69
|
+
→ `tools/call` → structured result — with streaming, cancellation, and approval layered on. You don't
|
|
70
|
+
implement the wire protocol; you declare tools and write handlers. (The live loop is the staged part.)
|
|
71
|
+
|
|
72
|
+
## Security
|
|
73
|
+
|
|
74
|
+
Identical to server functions — nothing new to reason about:
|
|
75
|
+
- **`visibility`** — who may call (admin/public).
|
|
76
|
+
- **`authority`** — whose identity `ctx.sl` carries (`caller` vs `collection`).
|
|
77
|
+
- **`capabilities`** — least-privilege, capped even when elevated.
|
|
78
|
+
- **`agent.approval: "require"`** — the agent must get human confirmation before invoking (use for
|
|
79
|
+
destructive or public-facing actions).
|
|
80
|
+
|
|
81
|
+
Untrusted third-party tools run in the platform's isolated runner — the same boundary as untrusted
|
|
82
|
+
server functions.
|
|
83
|
+
|
|
84
|
+
## Replacing `app.admin.json` AI setup
|
|
85
|
+
|
|
86
|
+
This supersedes the `app.admin.json` "AI setup" schema-extraction model (the outer agent reading a
|
|
87
|
+
schema and handing back JSON blobs). Instead the app owns its domain logic and exposes **real,
|
|
88
|
+
capability-scoped actions** the agent invokes — with multi-turn and streaming. The AI-schema path is
|
|
89
|
+
**deprecated as of V2** (still works through the V2 line; removed later).
|
|
90
|
+
|
|
91
|
+
## What ships now vs staged
|
|
92
|
+
|
|
93
|
+
| Now (V2) | Staged (on the platform agent) |
|
|
94
|
+
|---|---|
|
|
95
|
+
| The `agent` declaration in the manifest | The live agent conversation calling your tools |
|
|
96
|
+
| SDK types for it; tool descriptors surfaced for discovery | `tools/call` streaming, cancellation, cross-app toolbelt arbitration |
|
|
97
|
+
| Your handlers run today via http/event | Human-approval UX for `approval: "require"` |
|
|
98
|
+
|
|
99
|
+
## Adopting this in an existing app
|
|
100
|
+
|
|
101
|
+
Do it in order — each step is a drop-in migration prompt (see the migration steps that ship with the
|
|
102
|
+
SDK). Roughly:
|
|
103
|
+
|
|
104
|
+
1. **Server functions** — add the `functions` block + build target + test harness (if the app
|
|
105
|
+
doesn't have them). See [server-functions.md](server-functions.md).
|
|
106
|
+
2. **Expose tools** — add the `agent` block to the functions you want the agent to call; give each a
|
|
107
|
+
title/description/input schema and an approval mode.
|
|
108
|
+
3. **Retire `app.admin.json` AI setup** — move any AI-authoring behaviour to tools; drop the
|
|
109
|
+
AI-schema block.
|
|
110
|
+
|
|
111
|
+
Steps 1–2 are safe to ship now; step 3 as you migrate each app off the old AI path.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Deploying & registering an app
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
> `latest` remains 1.x.
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Part of the installable-app platform. Install
|
|
4
|
+
> `@proveanything/smartlinks@^2`.
|
|
6
5
|
|
|
7
6
|
A SmartLinks app is a bundle (widgets, containers, and — new — [server functions](server-functions.md))
|
|
8
7
|
described by an `app.manifest.json`. Deploying an app has two halves:
|
|
@@ -56,6 +55,12 @@ no secret; it's rate-limited and de-duped per app.
|
|
|
56
55
|
recorded (that's how we know what to fetch, and the `id` you ping with must match). Ask the platform
|
|
57
56
|
owner to register the app once; after that, every Publish auto-updates dev.
|
|
58
57
|
|
|
58
|
+
**If a publish doesn't appear:** because the ping is fire-and-forget, a failure (e.g. the build
|
|
59
|
+
hash never went live, or an invalid manifest) happens in the background — it won't show in your
|
|
60
|
+
build output. Every attempt (success *and* failure, with the reason) is recorded, and the platform
|
|
61
|
+
owner can see it in the console at **Admin → App Registry → Recent activity**. That's the place to
|
|
62
|
+
look for "I hit Publish but dev didn't change."
|
|
63
|
+
|
|
59
64
|
### B. From local / CI / Claude — `smartlinks-publish`
|
|
60
65
|
|
|
61
66
|
If you build somewhere you control (so you can hold a dev key locally — never committed), push the
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Host dependency contract (R5)
|
|
2
|
+
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). The portal host (R5) provides a fixed set of runtime
|
|
4
|
+
> libraries as window globals. Micro-apps **externalise** these and resolve them from the host —
|
|
5
|
+
> they must **not** bundle their own copies. This keeps one instance of React (and friends) on the
|
|
6
|
+
> page and keeps bundles small.
|
|
7
|
+
|
|
8
|
+
## The one rule that matters: externalise, never bundle
|
|
9
|
+
|
|
10
|
+
A container/widget/executor bundle **must externalise `react`, `react-dom`, and every shared
|
|
11
|
+
dependency below**, resolving them from the host globals. **Bundling your own React is the one
|
|
12
|
+
hard failure** — two React instances on the page → hooks break → crash. (Bundling `liquidjs` or
|
|
13
|
+
another shared lib is wasteful and can double-load, but React is the fatal one.)
|
|
14
|
+
|
|
15
|
+
React-18-built bundles keep working on the R5 host: they externalise React and run against the
|
|
16
|
+
host's **React 19** runtime. A bundle compiled against React 18 *typings* runs fine on the 19
|
|
17
|
+
*runtime* — see backwards-compatibility below.
|
|
18
|
+
|
|
19
|
+
## Vite / Rollup config
|
|
20
|
+
|
|
21
|
+
Externalise the shared deps and map each to its window global:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts (library build for a container/widget/executor)
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
build: {
|
|
28
|
+
lib: { entry: 'src/index.tsx', formats: ['umd'], name: 'MyApp', fileName: () => 'widgets.umd.js' },
|
|
29
|
+
rollupOptions: {
|
|
30
|
+
// Everything the host provides — do NOT bundle these.
|
|
31
|
+
external: [
|
|
32
|
+
'react', 'react-dom', 'react/jsx-runtime',
|
|
33
|
+
'@proveanything/smartlinks',
|
|
34
|
+
'react-router-dom', '@tanstack/react-query',
|
|
35
|
+
'lucide-react', 'date-fns', 'liquidjs', 'class-variance-authority',
|
|
36
|
+
'@radix-ui/react-slot', '@radix-ui/react-dialog', '@radix-ui/react-popover',
|
|
37
|
+
'@radix-ui/react-tooltip', '@radix-ui/react-tabs', '@radix-ui/react-accordion',
|
|
38
|
+
'@radix-ui/react-select', '@radix-ui/react-scroll-area', '@radix-ui/react-label',
|
|
39
|
+
'@radix-ui/react-toast', '@radix-ui/react-progress', '@radix-ui/react-avatar',
|
|
40
|
+
],
|
|
41
|
+
output: {
|
|
42
|
+
globals: {
|
|
43
|
+
'react': 'React', 'react-dom': 'ReactDOM', 'react/jsx-runtime': 'jsxRuntime',
|
|
44
|
+
'@proveanything/smartlinks': 'SL',
|
|
45
|
+
'react-router-dom': 'ReactRouterDOM', '@tanstack/react-query': 'ReactQuery',
|
|
46
|
+
'lucide-react': 'LucideReact', 'date-fns': 'dateFns', 'liquidjs': 'LiquidJS',
|
|
47
|
+
'class-variance-authority': 'CVA',
|
|
48
|
+
'@radix-ui/react-slot': 'RadixSlot', '@radix-ui/react-dialog': 'RadixDialog',
|
|
49
|
+
'@radix-ui/react-popover': 'RadixPopover', '@radix-ui/react-tooltip': 'RadixTooltip',
|
|
50
|
+
'@radix-ui/react-tabs': 'RadixTabs', '@radix-ui/react-accordion': 'RadixAccordion',
|
|
51
|
+
'@radix-ui/react-select': 'RadixSelect', '@radix-ui/react-scroll-area': 'RadixScrollArea',
|
|
52
|
+
'@radix-ui/react-label': 'RadixLabel', '@radix-ui/react-toast': 'RadixToast',
|
|
53
|
+
'@radix-ui/react-progress': 'RadixProgress', '@radix-ui/react-avatar': 'RadixAvatar',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Host-provided globals (build against versions ≤ these)
|
|
62
|
+
|
|
63
|
+
| Import | Window global | Host provides (R5) |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `react` | `React` | 19.3 (accepts 18.3 builds) |
|
|
66
|
+
| `react-dom` | `ReactDOM` | 19.3 (accepts 18.3 builds) |
|
|
67
|
+
| `react/jsx-runtime` | `jsxRuntime` | 19.3 |
|
|
68
|
+
| `@proveanything/smartlinks` | `SL` | 2.0.5 |
|
|
69
|
+
| `react-router-dom` | `ReactRouterDOM` | 7.18 (accepts 6.x builds) |
|
|
70
|
+
| `@tanstack/react-query` | `ReactQuery` | 5.103 |
|
|
71
|
+
| `lucide-react` | `LucideReact` | 1.47 |
|
|
72
|
+
| `date-fns` | `dateFns` | 4.4 |
|
|
73
|
+
| `liquidjs` | `LiquidJS` | 10.27+ |
|
|
74
|
+
| `class-variance-authority` | `CVA` | 0.7 |
|
|
75
|
+
| `@radix-ui/react-slot` | `RadixSlot` | 1.2.4 |
|
|
76
|
+
| `@radix-ui/react-dialog` | `RadixDialog` | 1.1.23 |
|
|
77
|
+
| `@radix-ui/react-popover` | `RadixPopover` | 1.1.1 |
|
|
78
|
+
| `@radix-ui/react-tooltip` | `RadixTooltip` | 1.2.8 |
|
|
79
|
+
| `@radix-ui/react-tabs` | `RadixTabs` | 1.1.13 |
|
|
80
|
+
| `@radix-ui/react-accordion` | `RadixAccordion` | 1.2.12 |
|
|
81
|
+
| `@radix-ui/react-select` | `RadixSelect` | 2.3.7 |
|
|
82
|
+
| `@radix-ui/react-scroll-area` | `RadixScrollArea` | 1.2.10 |
|
|
83
|
+
| `@radix-ui/react-label` | `RadixLabel` | 2.1.8 |
|
|
84
|
+
| `@radix-ui/react-toast` | `RadixToast` | 1.2.15 |
|
|
85
|
+
| `@radix-ui/react-progress` | `RadixProgress` | 1.1.8 |
|
|
86
|
+
| `@radix-ui/react-avatar` | `RadixAvatar` | 1.1.11 |
|
|
87
|
+
|
|
88
|
+
`liquidjs` is **host-provided** — externalise it, don't ship a second copy (frequently missed).
|
|
89
|
+
|
|
90
|
+
## Backwards compatibility
|
|
91
|
+
|
|
92
|
+
React-18-built containers and widgets keep working unchanged on R5. A pre-existing bundle only
|
|
93
|
+
breaks if it:
|
|
94
|
+
|
|
95
|
+
- calls `ReactDOM.render` / `hydrate` / `unmountComponentAtNode` (self-mounting — containers are
|
|
96
|
+
mounted by the host, so this only affects apps that mount themselves);
|
|
97
|
+
- relies on `defaultProps` / `propTypes` on **function** components (React 19 silently ignores
|
|
98
|
+
these → missing defaults, not a crash);
|
|
99
|
+
- uses string refs, `findDOMNode`, or legacy context;
|
|
100
|
+
- **bundles its own React** instead of externalising it → two instances → crash (the one hard
|
|
101
|
+
failure);
|
|
102
|
+
- imports a `lucide-react` icon renamed/removed in the 0.x → 1.x move.
|
|
103
|
+
|
|
104
|
+
## Tailwind is *not* part of the contract
|
|
105
|
+
|
|
106
|
+
Bundles ship their own compiled CSS, so the host's Tailwind version is irrelevant to them. A
|
|
107
|
+
micro-app can stay on **Tailwind 3 indefinitely**, or adopt Tailwind 4 — its choice. (The starter
|
|
108
|
+
app ships the Tailwind 4 CSS-first layout as the default; see its README.)
|
|
109
|
+
|
|
110
|
+
## The R5 host stack (reference)
|
|
111
|
+
|
|
112
|
+
React **19.3** · Vite **8.3** · react-router-dom **7.18** · Tailwind **4.3** (CSS-first) ·
|
|
113
|
+
TypeScript **6.0** · ESLint **10.11** · `@proveanything/smartlinks` **2.0.5** ·
|
|
114
|
+
`@proveanything/smartlinks-utils-ui` **1.16.4** · liquidjs **10.29**.
|
|
115
|
+
|
|
116
|
+
> **TypeScript 7** (the native/Go compiler) is **deliberately deferred** — tooling hasn't settled.
|
|
117
|
+
> Target **TS 6** for R5; it compiles existing code with no source changes.
|
|
118
|
+
|
|
119
|
+
## Security floor
|
|
120
|
+
|
|
121
|
+
An R5 app must ship with **`npm audit` reporting zero vulnerabilities**. Run it against the real
|
|
122
|
+
registry — `npm audit --registry=https://registry.npmjs.org` — because the sandbox mirror doesn't
|
|
123
|
+
implement the audit endpoint. Two high-severity advisories are already pinned out in the R5 set and
|
|
124
|
+
must stay pinned:
|
|
125
|
+
|
|
126
|
+
- **`react-router` 7.12.0–7.18.1** — RSC-mode CSRF bypass. Build against **7.18.4+** (the host
|
|
127
|
+
provides ≥7.18.4). Never ship a router below 7.18.4.
|
|
128
|
+
- **`browserslist` ≤4.28.6** — unbounded memory growth / prototype write. It's a transitive build
|
|
129
|
+
dependency, so pin it with an `overrides` entry (`"overrides": { "browserslist": "^4.29" }`), not
|
|
130
|
+
a direct dependency.
|
|
131
|
+
|
|
132
|
+
Both are compile-time/build-time concerns for the app's own toolchain; neither is a host global.
|
package/dist/docs/overview.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SmartLinks Microapp Development Guide
|
|
2
2
|
|
|
3
|
-
> **Platform revision:**
|
|
3
|
+
> **Platform revision:** R5 · **SDK:** `@proveanything/smartlinks@^2` (current `latest`, 2.0.5) · React 19 · Vite 8 · Tailwind 4
|
|
4
4
|
> **Last updated:** 2026-03-03
|
|
5
5
|
|
|
6
6
|
---
|
|
@@ -64,7 +64,9 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
64
64
|
| **Mobile Admin Container** | `docs/mobile-admin-container.md` | Building a separate Capacitor-aware mobile admin bundle for field operators |
|
|
65
65
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
66
66
|
| **Server Functions** | `docs/server-functions.md` | App-authored server-side functions `(ctx, event) ⇒ result`: security model, runtime surface, invoking |
|
|
67
|
+
| **Agent Tools** | `docs/agent-tools.md` | Exposing your app's actions to the SmartLinks agent — an MCP facade over server functions; declare now, live loop staged; replaces `app.admin.json` AI setup |
|
|
67
68
|
| **Deploying & Registering** | `docs/deploying-apps.md` | Getting your app into the platform: fast dev publish, channels, deploy keys, registering releases |
|
|
69
|
+
| **Host Dependency Contract (R5)** | `docs/host-dependency-contract.md` | The libraries the host provides (React 19, Router 7, Radix, liquidjs, …), the externalise-don't-bundle rule + Vite config, and React-18 backwards-compat |
|
|
68
70
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
69
71
|
| **Portal Back Button** | `docs/portal-back-button.md` | Hierarchy-aware "up" navigation inside embedded apps |
|
|
70
72
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
package/dist/docs/sequences.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sequences & claim-order allocation
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Install `@proveanything/smartlinks@^2`.
|
|
4
4
|
|
|
5
5
|
A **sequence** hands out a guaranteed-unique, monotonic number — `1, 2, 3, …` — and stamps it
|
|
6
6
|
onto a record. It's the primitive behind raffle tickets, "you're the Nth to claim", queue
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Server functions ("edge functions")
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
> then. Published under the npm `next` tag; `latest` remains 1.x.
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Part of the installable-app platform
|
|
4
|
+
> (author → register → install → run → test). Install `@proveanything/smartlinks@^2`.
|
|
6
5
|
|
|
7
6
|
A **server function** is arbitrary server-side JavaScript your app deploys directly into
|
|
8
7
|
SmartLinks. It runs on the SmartLinks servers — with access to the full SDK, to your app's
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { initializeApi, isInitialized, hasAuthCredentials, configureSdkCache, invalidateCache, getHttpCacheDiagnostics, resetHttpCacheDiagnostics, request, post, put, patch, del, sendCustomProxyMessage, getApiHeaders, isProxyEnabled, setBearerToken, getBearerToken, setGrantToken, getGrantToken } from "./http";
|
|
1
|
+
export { initializeApi, isInitialized, hasAuthCredentials, configureSdkCache, invalidateCache, getHttpCacheDiagnostics, resetHttpCacheDiagnostics, request, post, put, patch, del, sendCustomProxyMessage, getApiHeaders, getBaseURL, isProxyEnabled, setBearerToken, getBearerToken, setGrantToken, getGrantToken } from "./http";
|
|
2
2
|
export * from "./api";
|
|
3
3
|
export * from "./types";
|
|
4
4
|
export { iframe } from "./iframe";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
// Top-level entrypoint of the npm package. Re-export initializeApi + all namespaces.
|
|
3
|
-
export { initializeApi, isInitialized, hasAuthCredentials, configureSdkCache, invalidateCache, getHttpCacheDiagnostics, resetHttpCacheDiagnostics, request, post, put, patch, del, sendCustomProxyMessage, getApiHeaders, isProxyEnabled, setBearerToken, getBearerToken, setGrantToken, getGrantToken } from "./http";
|
|
3
|
+
export { initializeApi, isInitialized, hasAuthCredentials, configureSdkCache, invalidateCache, getHttpCacheDiagnostics, resetHttpCacheDiagnostics, request, post, put, patch, del, sendCustomProxyMessage, getApiHeaders, getBaseURL, isProxyEnabled, setBearerToken, getBearerToken, setGrantToken, getGrantToken } from "./http";
|
|
4
4
|
export * from "./api";
|
|
5
5
|
export * from "./types";
|
|
6
6
|
// Iframe namespace
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Agent tools — exposing your app's actions to the SmartLinks agent
|
|
2
|
+
|
|
3
|
+
> **SmartLinks SDK 2.x.** Redraft of the earlier "Agent Skills & Tools" RFC, now built on
|
|
4
|
+
> [server functions](server-functions.md). **Staged rollout:** the *declaration + discovery* ship in
|
|
5
|
+
> V2 (do them now); the *live agent loop* (a parent chat agent calling your tools) lights up when the
|
|
6
|
+
> platform agent can consume them. Everything you declare is useful before then — a tool is a server
|
|
7
|
+
> function, already invocable via http/event.
|
|
8
|
+
|
|
9
|
+
## The model: app-authored capabilities
|
|
10
|
+
|
|
11
|
+
An app declares **capabilities** — app-authored logic the platform runs, each with a declared
|
|
12
|
+
security envelope (`visibility` / `authority` / `capabilities`). There is **one engine** (the
|
|
13
|
+
server-functions runtime) and several **triggers** into it: `http`, `event`, `cron`, and **`agent`**.
|
|
14
|
+
|
|
15
|
+
**An agent tool is not a new runtime — it's an MCP-shaped facade over a capability.** By default a
|
|
16
|
+
tool *is a server function*: the agent's `tools/call` routes into the same `(ctx, event) => result`
|
|
17
|
+
with the same capability envelope. You author the logic once; the agent is just another caller.
|
|
18
|
+
|
|
19
|
+
Use a **client tool** (a `postMessage` handler in your live admin iframe) only for genuinely
|
|
20
|
+
UI-coupled actions that must run in the open app. It is **not** the default — headless server
|
|
21
|
+
functions are, because they work whether or not your UI is mounted.
|
|
22
|
+
|
|
23
|
+
## Declaring a tool
|
|
24
|
+
|
|
25
|
+
You already declare server functions in `app.manifest.json` (see
|
|
26
|
+
[server-functions.md](server-functions.md)). Mark one **agent-invocable** and give the agent what it
|
|
27
|
+
needs to call it — a description and an input schema:
|
|
28
|
+
|
|
29
|
+
```jsonc
|
|
30
|
+
{
|
|
31
|
+
"functions": {
|
|
32
|
+
"files": { "js": { "umd": "dist/functions.umd.js" } },
|
|
33
|
+
"definitions": [
|
|
34
|
+
{
|
|
35
|
+
"name": "createFaq",
|
|
36
|
+
"trigger": { "type": "http", "methods": ["POST"] },
|
|
37
|
+
"visibility": "admin",
|
|
38
|
+
"authority": "collection",
|
|
39
|
+
"capabilities": ["sl:records:write"],
|
|
40
|
+
|
|
41
|
+
// ── makes it an agent tool ──
|
|
42
|
+
"agent": {
|
|
43
|
+
"tool": true,
|
|
44
|
+
"title": "Create an FAQ entry",
|
|
45
|
+
"description": "Add a question/answer to this collection's FAQ. Use when the user asks to add or draft an FAQ.",
|
|
46
|
+
"input": { /* JSON Schema for the arguments the agent supplies as event.body */ },
|
|
47
|
+
"approval": "auto" // auto | require (require = human confirmation before the call)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The handler is an ordinary server function — the agent-supplied arguments arrive as `event.body`:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
export async function createFaq(ctx, event) {
|
|
59
|
+
const { question, answer } = event.body || {}
|
|
60
|
+
// validate, then act with your declared authority/capabilities:
|
|
61
|
+
const rec = await ctx.sl.appRecords.create({ recordType: 'faq', data: { question, answer } })
|
|
62
|
+
return { id: rec.id }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## What the agent sees (MCP shape)
|
|
67
|
+
|
|
68
|
+
The platform derives an **MCP tool list** from your manifest and drives the standard flow — `tools/list`
|
|
69
|
+
→ `tools/call` → structured result — with streaming, cancellation, and approval layered on. You don't
|
|
70
|
+
implement the wire protocol; you declare tools and write handlers. (The live loop is the staged part.)
|
|
71
|
+
|
|
72
|
+
## Security
|
|
73
|
+
|
|
74
|
+
Identical to server functions — nothing new to reason about:
|
|
75
|
+
- **`visibility`** — who may call (admin/public).
|
|
76
|
+
- **`authority`** — whose identity `ctx.sl` carries (`caller` vs `collection`).
|
|
77
|
+
- **`capabilities`** — least-privilege, capped even when elevated.
|
|
78
|
+
- **`agent.approval: "require"`** — the agent must get human confirmation before invoking (use for
|
|
79
|
+
destructive or public-facing actions).
|
|
80
|
+
|
|
81
|
+
Untrusted third-party tools run in the platform's isolated runner — the same boundary as untrusted
|
|
82
|
+
server functions.
|
|
83
|
+
|
|
84
|
+
## Replacing `app.admin.json` AI setup
|
|
85
|
+
|
|
86
|
+
This supersedes the `app.admin.json` "AI setup" schema-extraction model (the outer agent reading a
|
|
87
|
+
schema and handing back JSON blobs). Instead the app owns its domain logic and exposes **real,
|
|
88
|
+
capability-scoped actions** the agent invokes — with multi-turn and streaming. The AI-schema path is
|
|
89
|
+
**deprecated as of V2** (still works through the V2 line; removed later).
|
|
90
|
+
|
|
91
|
+
## What ships now vs staged
|
|
92
|
+
|
|
93
|
+
| Now (V2) | Staged (on the platform agent) |
|
|
94
|
+
|---|---|
|
|
95
|
+
| The `agent` declaration in the manifest | The live agent conversation calling your tools |
|
|
96
|
+
| SDK types for it; tool descriptors surfaced for discovery | `tools/call` streaming, cancellation, cross-app toolbelt arbitration |
|
|
97
|
+
| Your handlers run today via http/event | Human-approval UX for `approval: "require"` |
|
|
98
|
+
|
|
99
|
+
## Adopting this in an existing app
|
|
100
|
+
|
|
101
|
+
Do it in order — each step is a drop-in migration prompt (see the migration steps that ship with the
|
|
102
|
+
SDK). Roughly:
|
|
103
|
+
|
|
104
|
+
1. **Server functions** — add the `functions` block + build target + test harness (if the app
|
|
105
|
+
doesn't have them). See [server-functions.md](server-functions.md).
|
|
106
|
+
2. **Expose tools** — add the `agent` block to the functions you want the agent to call; give each a
|
|
107
|
+
title/description/input schema and an approval mode.
|
|
108
|
+
3. **Retire `app.admin.json` AI setup** — move any AI-authoring behaviour to tools; drop the
|
|
109
|
+
AI-schema block.
|
|
110
|
+
|
|
111
|
+
Steps 1–2 are safe to ship now; step 3 as you migrate each app off the old AI path.
|
package/docs/deploying-apps.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Deploying & registering an app
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
> `latest` remains 1.x.
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Part of the installable-app platform. Install
|
|
4
|
+
> `@proveanything/smartlinks@^2`.
|
|
6
5
|
|
|
7
6
|
A SmartLinks app is a bundle (widgets, containers, and — new — [server functions](server-functions.md))
|
|
8
7
|
described by an `app.manifest.json`. Deploying an app has two halves:
|
|
@@ -56,6 +55,12 @@ no secret; it's rate-limited and de-duped per app.
|
|
|
56
55
|
recorded (that's how we know what to fetch, and the `id` you ping with must match). Ask the platform
|
|
57
56
|
owner to register the app once; after that, every Publish auto-updates dev.
|
|
58
57
|
|
|
58
|
+
**If a publish doesn't appear:** because the ping is fire-and-forget, a failure (e.g. the build
|
|
59
|
+
hash never went live, or an invalid manifest) happens in the background — it won't show in your
|
|
60
|
+
build output. Every attempt (success *and* failure, with the reason) is recorded, and the platform
|
|
61
|
+
owner can see it in the console at **Admin → App Registry → Recent activity**. That's the place to
|
|
62
|
+
look for "I hit Publish but dev didn't change."
|
|
63
|
+
|
|
59
64
|
### B. From local / CI / Claude — `smartlinks-publish`
|
|
60
65
|
|
|
61
66
|
If you build somewhere you control (so you can hold a dev key locally — never committed), push the
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Host dependency contract (R5)
|
|
2
|
+
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). The portal host (R5) provides a fixed set of runtime
|
|
4
|
+
> libraries as window globals. Micro-apps **externalise** these and resolve them from the host —
|
|
5
|
+
> they must **not** bundle their own copies. This keeps one instance of React (and friends) on the
|
|
6
|
+
> page and keeps bundles small.
|
|
7
|
+
|
|
8
|
+
## The one rule that matters: externalise, never bundle
|
|
9
|
+
|
|
10
|
+
A container/widget/executor bundle **must externalise `react`, `react-dom`, and every shared
|
|
11
|
+
dependency below**, resolving them from the host globals. **Bundling your own React is the one
|
|
12
|
+
hard failure** — two React instances on the page → hooks break → crash. (Bundling `liquidjs` or
|
|
13
|
+
another shared lib is wasteful and can double-load, but React is the fatal one.)
|
|
14
|
+
|
|
15
|
+
React-18-built bundles keep working on the R5 host: they externalise React and run against the
|
|
16
|
+
host's **React 19** runtime. A bundle compiled against React 18 *typings* runs fine on the 19
|
|
17
|
+
*runtime* — see backwards-compatibility below.
|
|
18
|
+
|
|
19
|
+
## Vite / Rollup config
|
|
20
|
+
|
|
21
|
+
Externalise the shared deps and map each to its window global:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// vite.config.ts (library build for a container/widget/executor)
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
build: {
|
|
28
|
+
lib: { entry: 'src/index.tsx', formats: ['umd'], name: 'MyApp', fileName: () => 'widgets.umd.js' },
|
|
29
|
+
rollupOptions: {
|
|
30
|
+
// Everything the host provides — do NOT bundle these.
|
|
31
|
+
external: [
|
|
32
|
+
'react', 'react-dom', 'react/jsx-runtime',
|
|
33
|
+
'@proveanything/smartlinks',
|
|
34
|
+
'react-router-dom', '@tanstack/react-query',
|
|
35
|
+
'lucide-react', 'date-fns', 'liquidjs', 'class-variance-authority',
|
|
36
|
+
'@radix-ui/react-slot', '@radix-ui/react-dialog', '@radix-ui/react-popover',
|
|
37
|
+
'@radix-ui/react-tooltip', '@radix-ui/react-tabs', '@radix-ui/react-accordion',
|
|
38
|
+
'@radix-ui/react-select', '@radix-ui/react-scroll-area', '@radix-ui/react-label',
|
|
39
|
+
'@radix-ui/react-toast', '@radix-ui/react-progress', '@radix-ui/react-avatar',
|
|
40
|
+
],
|
|
41
|
+
output: {
|
|
42
|
+
globals: {
|
|
43
|
+
'react': 'React', 'react-dom': 'ReactDOM', 'react/jsx-runtime': 'jsxRuntime',
|
|
44
|
+
'@proveanything/smartlinks': 'SL',
|
|
45
|
+
'react-router-dom': 'ReactRouterDOM', '@tanstack/react-query': 'ReactQuery',
|
|
46
|
+
'lucide-react': 'LucideReact', 'date-fns': 'dateFns', 'liquidjs': 'LiquidJS',
|
|
47
|
+
'class-variance-authority': 'CVA',
|
|
48
|
+
'@radix-ui/react-slot': 'RadixSlot', '@radix-ui/react-dialog': 'RadixDialog',
|
|
49
|
+
'@radix-ui/react-popover': 'RadixPopover', '@radix-ui/react-tooltip': 'RadixTooltip',
|
|
50
|
+
'@radix-ui/react-tabs': 'RadixTabs', '@radix-ui/react-accordion': 'RadixAccordion',
|
|
51
|
+
'@radix-ui/react-select': 'RadixSelect', '@radix-ui/react-scroll-area': 'RadixScrollArea',
|
|
52
|
+
'@radix-ui/react-label': 'RadixLabel', '@radix-ui/react-toast': 'RadixToast',
|
|
53
|
+
'@radix-ui/react-progress': 'RadixProgress', '@radix-ui/react-avatar': 'RadixAvatar',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Host-provided globals (build against versions ≤ these)
|
|
62
|
+
|
|
63
|
+
| Import | Window global | Host provides (R5) |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `react` | `React` | 19.3 (accepts 18.3 builds) |
|
|
66
|
+
| `react-dom` | `ReactDOM` | 19.3 (accepts 18.3 builds) |
|
|
67
|
+
| `react/jsx-runtime` | `jsxRuntime` | 19.3 |
|
|
68
|
+
| `@proveanything/smartlinks` | `SL` | 2.0.5 |
|
|
69
|
+
| `react-router-dom` | `ReactRouterDOM` | 7.18 (accepts 6.x builds) |
|
|
70
|
+
| `@tanstack/react-query` | `ReactQuery` | 5.103 |
|
|
71
|
+
| `lucide-react` | `LucideReact` | 1.47 |
|
|
72
|
+
| `date-fns` | `dateFns` | 4.4 |
|
|
73
|
+
| `liquidjs` | `LiquidJS` | 10.27+ |
|
|
74
|
+
| `class-variance-authority` | `CVA` | 0.7 |
|
|
75
|
+
| `@radix-ui/react-slot` | `RadixSlot` | 1.2.4 |
|
|
76
|
+
| `@radix-ui/react-dialog` | `RadixDialog` | 1.1.23 |
|
|
77
|
+
| `@radix-ui/react-popover` | `RadixPopover` | 1.1.1 |
|
|
78
|
+
| `@radix-ui/react-tooltip` | `RadixTooltip` | 1.2.8 |
|
|
79
|
+
| `@radix-ui/react-tabs` | `RadixTabs` | 1.1.13 |
|
|
80
|
+
| `@radix-ui/react-accordion` | `RadixAccordion` | 1.2.12 |
|
|
81
|
+
| `@radix-ui/react-select` | `RadixSelect` | 2.3.7 |
|
|
82
|
+
| `@radix-ui/react-scroll-area` | `RadixScrollArea` | 1.2.10 |
|
|
83
|
+
| `@radix-ui/react-label` | `RadixLabel` | 2.1.8 |
|
|
84
|
+
| `@radix-ui/react-toast` | `RadixToast` | 1.2.15 |
|
|
85
|
+
| `@radix-ui/react-progress` | `RadixProgress` | 1.1.8 |
|
|
86
|
+
| `@radix-ui/react-avatar` | `RadixAvatar` | 1.1.11 |
|
|
87
|
+
|
|
88
|
+
`liquidjs` is **host-provided** — externalise it, don't ship a second copy (frequently missed).
|
|
89
|
+
|
|
90
|
+
## Backwards compatibility
|
|
91
|
+
|
|
92
|
+
React-18-built containers and widgets keep working unchanged on R5. A pre-existing bundle only
|
|
93
|
+
breaks if it:
|
|
94
|
+
|
|
95
|
+
- calls `ReactDOM.render` / `hydrate` / `unmountComponentAtNode` (self-mounting — containers are
|
|
96
|
+
mounted by the host, so this only affects apps that mount themselves);
|
|
97
|
+
- relies on `defaultProps` / `propTypes` on **function** components (React 19 silently ignores
|
|
98
|
+
these → missing defaults, not a crash);
|
|
99
|
+
- uses string refs, `findDOMNode`, or legacy context;
|
|
100
|
+
- **bundles its own React** instead of externalising it → two instances → crash (the one hard
|
|
101
|
+
failure);
|
|
102
|
+
- imports a `lucide-react` icon renamed/removed in the 0.x → 1.x move.
|
|
103
|
+
|
|
104
|
+
## Tailwind is *not* part of the contract
|
|
105
|
+
|
|
106
|
+
Bundles ship their own compiled CSS, so the host's Tailwind version is irrelevant to them. A
|
|
107
|
+
micro-app can stay on **Tailwind 3 indefinitely**, or adopt Tailwind 4 — its choice. (The starter
|
|
108
|
+
app ships the Tailwind 4 CSS-first layout as the default; see its README.)
|
|
109
|
+
|
|
110
|
+
## The R5 host stack (reference)
|
|
111
|
+
|
|
112
|
+
React **19.3** · Vite **8.3** · react-router-dom **7.18** · Tailwind **4.3** (CSS-first) ·
|
|
113
|
+
TypeScript **6.0** · ESLint **10.11** · `@proveanything/smartlinks` **2.0.5** ·
|
|
114
|
+
`@proveanything/smartlinks-utils-ui` **1.16.4** · liquidjs **10.29**.
|
|
115
|
+
|
|
116
|
+
> **TypeScript 7** (the native/Go compiler) is **deliberately deferred** — tooling hasn't settled.
|
|
117
|
+
> Target **TS 6** for R5; it compiles existing code with no source changes.
|
|
118
|
+
|
|
119
|
+
## Security floor
|
|
120
|
+
|
|
121
|
+
An R5 app must ship with **`npm audit` reporting zero vulnerabilities**. Run it against the real
|
|
122
|
+
registry — `npm audit --registry=https://registry.npmjs.org` — because the sandbox mirror doesn't
|
|
123
|
+
implement the audit endpoint. Two high-severity advisories are already pinned out in the R5 set and
|
|
124
|
+
must stay pinned:
|
|
125
|
+
|
|
126
|
+
- **`react-router` 7.12.0–7.18.1** — RSC-mode CSRF bypass. Build against **7.18.4+** (the host
|
|
127
|
+
provides ≥7.18.4). Never ship a router below 7.18.4.
|
|
128
|
+
- **`browserslist` ≤4.28.6** — unbounded memory growth / prototype write. It's a transitive build
|
|
129
|
+
dependency, so pin it with an `overrides` entry (`"overrides": { "browserslist": "^4.29" }`), not
|
|
130
|
+
a direct dependency.
|
|
131
|
+
|
|
132
|
+
Both are compile-time/build-time concerns for the app's own toolchain; neither is a host global.
|
package/docs/overview.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SmartLinks Microapp Development Guide
|
|
2
2
|
|
|
3
|
-
> **Platform revision:**
|
|
3
|
+
> **Platform revision:** R5 · **SDK:** `@proveanything/smartlinks@^2` (current `latest`, 2.0.5) · React 19 · Vite 8 · Tailwind 4
|
|
4
4
|
> **Last updated:** 2026-03-03
|
|
5
5
|
|
|
6
6
|
---
|
|
@@ -64,7 +64,9 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
|
|
|
64
64
|
| **Mobile Admin Container** | `docs/mobile-admin-container.md` | Building a separate Capacitor-aware mobile admin bundle for field operators |
|
|
65
65
|
| **Executors** | `docs/executor.md` | Building executor bundles for SEO, LLM content, programmatic config |
|
|
66
66
|
| **Server Functions** | `docs/server-functions.md` | App-authored server-side functions `(ctx, event) ⇒ result`: security model, runtime surface, invoking |
|
|
67
|
+
| **Agent Tools** | `docs/agent-tools.md` | Exposing your app's actions to the SmartLinks agent — an MCP facade over server functions; declare now, live loop staged; replaces `app.admin.json` AI setup |
|
|
67
68
|
| **Deploying & Registering** | `docs/deploying-apps.md` | Getting your app into the platform: fast dev publish, channels, deploy keys, registering releases |
|
|
69
|
+
| **Host Dependency Contract (R5)** | `docs/host-dependency-contract.md` | The libraries the host provides (React 19, Router 7, Radix, liquidjs, …), the externalise-don't-bundle rule + Vite config, and React-18 backwards-compat |
|
|
68
70
|
| **Deep Linking** | `docs/deep-link-discovery.md` | URL state management, navigable states, portal menus, AI nav |
|
|
69
71
|
| **Portal Back Button** | `docs/portal-back-button.md` | Hierarchy-aware "up" navigation inside embedded apps |
|
|
70
72
|
| **Portal Request Action** | `docs/portal-request-action.md` | Triggering portal built-in actions (__qrScanner, __share, __logout, etc.) from sub-apps |
|
package/docs/sequences.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sequences & claim-order allocation
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Install `@proveanything/smartlinks@^2`.
|
|
4
4
|
|
|
5
5
|
A **sequence** hands out a guaranteed-unique, monotonic number — `1, 2, 3, …` — and stamps it
|
|
6
6
|
onto a record. It's the primitive behind raffle tickets, "you're the Nth to claim", queue
|
package/docs/server-functions.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Server functions ("edge functions")
|
|
2
2
|
|
|
3
|
-
> **
|
|
4
|
-
>
|
|
5
|
-
> then. Published under the npm `next` tag; `latest` remains 1.x.
|
|
3
|
+
> **SmartLinks SDK 2.x** (current `latest`). Part of the installable-app platform
|
|
4
|
+
> (author → register → install → run → test). Install `@proveanything/smartlinks@^2`.
|
|
6
5
|
|
|
7
6
|
A **server function** is arbitrary server-side JavaScript your app deploys directly into
|
|
8
7
|
SmartLinks. It runs on the SmartLinks servers — with access to the full SDK, to your app's
|