@fias/create-fias-plugin 1.18.0 → 1.19.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/package.json +1 -1
- package/templates/default/AGENTS.md +116 -1
- package/templates/default/CLAUDE.md +116 -1
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- fias-sdk-guide-version: 2.
|
|
1
|
+
<!-- fias-sdk-guide-version: 2.27.0 -->
|
|
2
2
|
|
|
3
3
|
# FIAS Plugin Development Guide
|
|
4
4
|
|
|
@@ -283,6 +283,121 @@ Why this and not `createCollection` at startup: the runtime op is capped at **10
|
|
|
283
283
|
|
|
284
284
|
Re-publishing is safe: existing collections are left alone, a stricter role floor is applied, a looser one is refused, and a collection you stop declaring is kept (never deleted — it may hold user data). Keep using `createCollection` for collections whose names you only know at runtime.
|
|
285
285
|
|
|
286
|
+
### Letting agents use your data (`agentAccess`)
|
|
287
|
+
|
|
288
|
+
**Permission:** `data:agents` (requires `data:store`)
|
|
289
|
+
|
|
290
|
+
Your plugin can share some of its collections with AI agents — the platform's Fias AI assistant, and agents a user connects over MCP (Claude Code and others) — **while your plugin is closed**. Agents never run your code: they read and change documents through a fixed set of platform tools (`plugin_apps_list`, `plugin_app_describe`, `plugin_data_query`, `plugin_data_get`, `plugin_data_put`, `plugin_data_patch`, `plugin_data_delete`). Declare what you share in `fias-plugin.json`:
|
|
291
|
+
|
|
292
|
+
```jsonc
|
|
293
|
+
"permissions": ["data:store", "data:agents"],
|
|
294
|
+
"collections": [
|
|
295
|
+
{ "name": "scenes", "scope": "user" },
|
|
296
|
+
{ "name": "render_requests", "scope": "user" },
|
|
297
|
+
{ "name": "previews", "scope": "user" }
|
|
298
|
+
],
|
|
299
|
+
"agentAccess": {
|
|
300
|
+
"description": "Puppet cut scenes: a cast, scene-frames and spoken lines.",
|
|
301
|
+
"collections": {
|
|
302
|
+
"scenes": {
|
|
303
|
+
"access": "read_write",
|
|
304
|
+
"description": "One cut scene per document, keyed by scene id.",
|
|
305
|
+
"schema": { "type": "object", "properties": { "title": { "type": "string" } }, "required": ["title"] }
|
|
306
|
+
},
|
|
307
|
+
"render_requests": {
|
|
308
|
+
"access": "read_write",
|
|
309
|
+
"description": "Ask the open app for stills of a scene at chosen times.",
|
|
310
|
+
"schema": {
|
|
311
|
+
"type": "object",
|
|
312
|
+
"properties": {
|
|
313
|
+
"scene": { "type": "string", "maxLength": 100 },
|
|
314
|
+
"times": { "type": "array", "items": { "type": "number", "minimum": 0 }, "minItems": 1, "maxItems": 12 },
|
|
315
|
+
"width": { "type": "integer", "minimum": 64, "maximum": 800 },
|
|
316
|
+
"status": { "enum": ["pending", "working", "done", "failed"] },
|
|
317
|
+
"error": { "type": "string", "maxLength": 2000 },
|
|
318
|
+
"previews": { "type": "integer", "minimum": 0, "maximum": 12 }
|
|
319
|
+
},
|
|
320
|
+
"required": ["scene", "times", "status"],
|
|
321
|
+
"additionalProperties": false
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
"previews": { "access": "read", "description": "Stills the open app rendered.", "images": ["/image"] }
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Rules (checked by `fias-dev validate` and at publish):
|
|
330
|
+
|
|
331
|
+
- `data:agents` and the `agentAccess` block go together: declaring the permission without a block is an error.
|
|
332
|
+
- Only collections you declare in `collections`, with `user` or `shared` scope. `read_write` only on `user` scope (a write to a `shared` collection would land in every user's copy), and it needs a `schema` — every agent write is validated against it. `workspace` collections can't be shared with agents yet.
|
|
333
|
+
- Schemas use a restricted JSON Schema 2020-12: `type`, `properties`, `required`, `additionalProperties`, `items`, `prefixItems`, `enum`, `const`, min/max bounds, `anyOf`/`oneOf`/`allOf`/`not`, `title`, `description`, `default`, `examples`, `format` (annotation only), and `$ref` to your own `#/$defs/…`. **No `pattern`** (regular expressions), no `propertyNames`, and no remote `$ref`. `uniqueItems` only on arrays whose `items` have a scalar `type`. A `$ref` may recurse only through `properties` / `items` / `additionalProperties` (a tree), never straight back to itself. ≤ 16 KB per schema, depth ≤ 12. If you generate schemas with Zod, note that `z.record()` emits `propertyNames` and `z.email()` / `z.uuid()` / `z.regex()` emit `pattern` — neither is accepted.
|
|
334
|
+
- Descriptions describe your data — they are not instructions to an AI. The publish review flags text that tells an agent what to do, links, and mentions of other apps.
|
|
335
|
+
- `images` names fields holding `{ "mimeType": "image/png" | "image/jpeg" | "image/webp", "data": "<base64>" }` (≤ 96 KB, ≤ 2000 px a side; every image is fully decoded before an agent sees it, and one that doesn't decode is replaced by an error). Agents receive them as pictures and can never write them — so in a `read_write` collection an image field must be optional, never `required`.
|
|
336
|
+
|
|
337
|
+
What agents can do: **Fias AI** acts as the signed-in user on what you declare. An **external agent** reaches your app only through an API key the user allowed to use it, collection by collection, read or read and change — and if you later share more, nothing new is reachable until the user allows it again. Every agent write is conditional on the version it read, so it can't silently overwrite a change made in your open UI; deletes always need the user's confirmation in Fias.
|
|
338
|
+
|
|
339
|
+
**Your obligation for `read_write` collections: validate what you load.** An agent — or text an agent was shown — may have written it. Treat those documents like any untrusted input; never evaluate or render a field as code or HTML.
|
|
340
|
+
|
|
341
|
+
**Answering an agent while open (previews).** Agents can't run your code, but your open plugin can do work for them through data. Watch a requests collection, do the work, and write the answer back:
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
const dataStore = useFiasDataStore();
|
|
345
|
+
const answerPending = useCallback(async () => {
|
|
346
|
+
const { documents } = await dataStore.query<RenderRequest>('render_requests', {
|
|
347
|
+
filters: [{ field: 'status', op: 'eq', value: 'pending' }],
|
|
348
|
+
limit: 5,
|
|
349
|
+
});
|
|
350
|
+
for (const req of documents) {
|
|
351
|
+
// Claim it first, so two open tabs never both do it.
|
|
352
|
+
const claimed = await dataStore
|
|
353
|
+
.batch(
|
|
354
|
+
[
|
|
355
|
+
{
|
|
356
|
+
op: 'put',
|
|
357
|
+
collection: 'render_requests',
|
|
358
|
+
key: req.key,
|
|
359
|
+
data: { ...req.data, status: 'working' },
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
{
|
|
363
|
+
preconditions: [
|
|
364
|
+
{ kind: 'version', collection: 'render_requests', key: req.key, version: req.version },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
)
|
|
368
|
+
.then(
|
|
369
|
+
() => true,
|
|
370
|
+
() => false,
|
|
371
|
+
);
|
|
372
|
+
if (!claimed) continue;
|
|
373
|
+
const stills = await renderStills(req.data); // your own code, bounded by your schema
|
|
374
|
+
for (const [i, image] of stills.entries()) {
|
|
375
|
+
await dataStore.put('previews', `${req.key}-${i}`, {
|
|
376
|
+
request: req.key,
|
|
377
|
+
t: req.data.times[i],
|
|
378
|
+
image,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
await dataStore.put('render_requests', req.key, {
|
|
382
|
+
...req.data,
|
|
383
|
+
status: 'done',
|
|
384
|
+
previews: stills.length,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}, [dataStore]);
|
|
388
|
+
useDataSubscription('render_requests', answerPending); // changes arrive at most once —
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
// so also check on open and every ~10 s
|
|
391
|
+
void answerPending();
|
|
392
|
+
const timer = setInterval(answerPending, 10_000);
|
|
393
|
+
return () => clearInterval(timer);
|
|
394
|
+
}, [answerPending]);
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The agent writes a request, waits on it with `plugin_data_get` (`afterVersion` + `waitSeconds`), and reads the stills back as images. Keep requests bounded in your schema (e.g. `"times": { "maxItems": 12 }`), prune old previews, and report failures in the request (e.g. `status: "failed"` with errors) so the agent can fix what it wrote. **Give both the requests and the results collections `user` scope** — in a `shared` collection any user of your app could plant a "result" that another user's agent then reads.
|
|
398
|
+
|
|
399
|
+
**Testing:** in the dev harness's Fias AI window, the read tools see your local `agentAccess` block against your dev data — ask "what scenes do I have?" to see what an agent sees before you publish.
|
|
400
|
+
|
|
286
401
|
**Rate limits (per minute):** `put` 120, `get` 300, `query` 60, `delete` 60, `batch` 30, `createCollection` 10
|
|
287
402
|
|
|
288
403
|
**Error handling:** Data store calls can reject on infrastructure errors or limit violations. Always use `try/catch`. Common error codes: `COLLECTION_NOT_FOUND`, `DOCUMENT_TOO_LARGE`, `STORAGE_LIMIT_REACHED`, `RATE_LIMIT`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- fias-sdk-guide-version: 2.
|
|
1
|
+
<!-- fias-sdk-guide-version: 2.27.0 -->
|
|
2
2
|
|
|
3
3
|
# FIAS Plugin Development Guide
|
|
4
4
|
|
|
@@ -283,6 +283,121 @@ Why this and not `createCollection` at startup: the runtime op is capped at **10
|
|
|
283
283
|
|
|
284
284
|
Re-publishing is safe: existing collections are left alone, a stricter role floor is applied, a looser one is refused, and a collection you stop declaring is kept (never deleted — it may hold user data). Keep using `createCollection` for collections whose names you only know at runtime.
|
|
285
285
|
|
|
286
|
+
### Letting agents use your data (`agentAccess`)
|
|
287
|
+
|
|
288
|
+
**Permission:** `data:agents` (requires `data:store`)
|
|
289
|
+
|
|
290
|
+
Your plugin can share some of its collections with AI agents — the platform's Fias AI assistant, and agents a user connects over MCP (Claude Code and others) — **while your plugin is closed**. Agents never run your code: they read and change documents through a fixed set of platform tools (`plugin_apps_list`, `plugin_app_describe`, `plugin_data_query`, `plugin_data_get`, `plugin_data_put`, `plugin_data_patch`, `plugin_data_delete`). Declare what you share in `fias-plugin.json`:
|
|
291
|
+
|
|
292
|
+
```jsonc
|
|
293
|
+
"permissions": ["data:store", "data:agents"],
|
|
294
|
+
"collections": [
|
|
295
|
+
{ "name": "scenes", "scope": "user" },
|
|
296
|
+
{ "name": "render_requests", "scope": "user" },
|
|
297
|
+
{ "name": "previews", "scope": "user" }
|
|
298
|
+
],
|
|
299
|
+
"agentAccess": {
|
|
300
|
+
"description": "Puppet cut scenes: a cast, scene-frames and spoken lines.",
|
|
301
|
+
"collections": {
|
|
302
|
+
"scenes": {
|
|
303
|
+
"access": "read_write",
|
|
304
|
+
"description": "One cut scene per document, keyed by scene id.",
|
|
305
|
+
"schema": { "type": "object", "properties": { "title": { "type": "string" } }, "required": ["title"] }
|
|
306
|
+
},
|
|
307
|
+
"render_requests": {
|
|
308
|
+
"access": "read_write",
|
|
309
|
+
"description": "Ask the open app for stills of a scene at chosen times.",
|
|
310
|
+
"schema": {
|
|
311
|
+
"type": "object",
|
|
312
|
+
"properties": {
|
|
313
|
+
"scene": { "type": "string", "maxLength": 100 },
|
|
314
|
+
"times": { "type": "array", "items": { "type": "number", "minimum": 0 }, "minItems": 1, "maxItems": 12 },
|
|
315
|
+
"width": { "type": "integer", "minimum": 64, "maximum": 800 },
|
|
316
|
+
"status": { "enum": ["pending", "working", "done", "failed"] },
|
|
317
|
+
"error": { "type": "string", "maxLength": 2000 },
|
|
318
|
+
"previews": { "type": "integer", "minimum": 0, "maximum": 12 }
|
|
319
|
+
},
|
|
320
|
+
"required": ["scene", "times", "status"],
|
|
321
|
+
"additionalProperties": false
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
"previews": { "access": "read", "description": "Stills the open app rendered.", "images": ["/image"] }
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Rules (checked by `fias-dev validate` and at publish):
|
|
330
|
+
|
|
331
|
+
- `data:agents` and the `agentAccess` block go together: declaring the permission without a block is an error.
|
|
332
|
+
- Only collections you declare in `collections`, with `user` or `shared` scope. `read_write` only on `user` scope (a write to a `shared` collection would land in every user's copy), and it needs a `schema` — every agent write is validated against it. `workspace` collections can't be shared with agents yet.
|
|
333
|
+
- Schemas use a restricted JSON Schema 2020-12: `type`, `properties`, `required`, `additionalProperties`, `items`, `prefixItems`, `enum`, `const`, min/max bounds, `anyOf`/`oneOf`/`allOf`/`not`, `title`, `description`, `default`, `examples`, `format` (annotation only), and `$ref` to your own `#/$defs/…`. **No `pattern`** (regular expressions), no `propertyNames`, and no remote `$ref`. `uniqueItems` only on arrays whose `items` have a scalar `type`. A `$ref` may recurse only through `properties` / `items` / `additionalProperties` (a tree), never straight back to itself. ≤ 16 KB per schema, depth ≤ 12. If you generate schemas with Zod, note that `z.record()` emits `propertyNames` and `z.email()` / `z.uuid()` / `z.regex()` emit `pattern` — neither is accepted.
|
|
334
|
+
- Descriptions describe your data — they are not instructions to an AI. The publish review flags text that tells an agent what to do, links, and mentions of other apps.
|
|
335
|
+
- `images` names fields holding `{ "mimeType": "image/png" | "image/jpeg" | "image/webp", "data": "<base64>" }` (≤ 96 KB, ≤ 2000 px a side; every image is fully decoded before an agent sees it, and one that doesn't decode is replaced by an error). Agents receive them as pictures and can never write them — so in a `read_write` collection an image field must be optional, never `required`.
|
|
336
|
+
|
|
337
|
+
What agents can do: **Fias AI** acts as the signed-in user on what you declare. An **external agent** reaches your app only through an API key the user allowed to use it, collection by collection, read or read and change — and if you later share more, nothing new is reachable until the user allows it again. Every agent write is conditional on the version it read, so it can't silently overwrite a change made in your open UI; deletes always need the user's confirmation in Fias.
|
|
338
|
+
|
|
339
|
+
**Your obligation for `read_write` collections: validate what you load.** An agent — or text an agent was shown — may have written it. Treat those documents like any untrusted input; never evaluate or render a field as code or HTML.
|
|
340
|
+
|
|
341
|
+
**Answering an agent while open (previews).** Agents can't run your code, but your open plugin can do work for them through data. Watch a requests collection, do the work, and write the answer back:
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
const dataStore = useFiasDataStore();
|
|
345
|
+
const answerPending = useCallback(async () => {
|
|
346
|
+
const { documents } = await dataStore.query<RenderRequest>('render_requests', {
|
|
347
|
+
filters: [{ field: 'status', op: 'eq', value: 'pending' }],
|
|
348
|
+
limit: 5,
|
|
349
|
+
});
|
|
350
|
+
for (const req of documents) {
|
|
351
|
+
// Claim it first, so two open tabs never both do it.
|
|
352
|
+
const claimed = await dataStore
|
|
353
|
+
.batch(
|
|
354
|
+
[
|
|
355
|
+
{
|
|
356
|
+
op: 'put',
|
|
357
|
+
collection: 'render_requests',
|
|
358
|
+
key: req.key,
|
|
359
|
+
data: { ...req.data, status: 'working' },
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
{
|
|
363
|
+
preconditions: [
|
|
364
|
+
{ kind: 'version', collection: 'render_requests', key: req.key, version: req.version },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
)
|
|
368
|
+
.then(
|
|
369
|
+
() => true,
|
|
370
|
+
() => false,
|
|
371
|
+
);
|
|
372
|
+
if (!claimed) continue;
|
|
373
|
+
const stills = await renderStills(req.data); // your own code, bounded by your schema
|
|
374
|
+
for (const [i, image] of stills.entries()) {
|
|
375
|
+
await dataStore.put('previews', `${req.key}-${i}`, {
|
|
376
|
+
request: req.key,
|
|
377
|
+
t: req.data.times[i],
|
|
378
|
+
image,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
await dataStore.put('render_requests', req.key, {
|
|
382
|
+
...req.data,
|
|
383
|
+
status: 'done',
|
|
384
|
+
previews: stills.length,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}, [dataStore]);
|
|
388
|
+
useDataSubscription('render_requests', answerPending); // changes arrive at most once —
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
// so also check on open and every ~10 s
|
|
391
|
+
void answerPending();
|
|
392
|
+
const timer = setInterval(answerPending, 10_000);
|
|
393
|
+
return () => clearInterval(timer);
|
|
394
|
+
}, [answerPending]);
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The agent writes a request, waits on it with `plugin_data_get` (`afterVersion` + `waitSeconds`), and reads the stills back as images. Keep requests bounded in your schema (e.g. `"times": { "maxItems": 12 }`), prune old previews, and report failures in the request (e.g. `status: "failed"` with errors) so the agent can fix what it wrote. **Give both the requests and the results collections `user` scope** — in a `shared` collection any user of your app could plant a "result" that another user's agent then reads.
|
|
398
|
+
|
|
399
|
+
**Testing:** in the dev harness's Fias AI window, the read tools see your local `agentAccess` block against your dev data — ask "what scenes do I have?" to see what an agent sees before you publish.
|
|
400
|
+
|
|
286
401
|
**Rate limits (per minute):** `put` 120, `get` 300, `query` 60, `delete` 60, `batch` 30, `createCollection` 10
|
|
287
402
|
|
|
288
403
|
**Error handling:** Data store calls can reject on infrastructure errors or limit violations. Always use `try/catch`. Common error codes: `COLLECTION_NOT_FOUND`, `DOCUMENT_TOO_LARGE`, `STORAGE_LIMIT_REACHED`, `RATE_LIMIT`.
|