@jskit-ai/agent-docs 0.1.207 → 0.1.208

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.
Files changed (35) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +534 -6
  2. package/guide/agent/app-extras/assistant-voice.md +16 -1
  3. package/guide/agent/app-extras/assistant.md +8 -0
  4. package/guide/agent/framework/application-operations.md +29 -0
  5. package/guide/agent/framework/crud-operations.md +100 -0
  6. package/package.json +1 -1
  7. package/patterns/feature-package/example/booking-engine/package.json +1 -1
  8. package/patterns/minimal-foundation/example/package.json +4 -4
  9. package/patterns/server-search.md +16 -0
  10. package/patterns/shell-foundation/example/package.json +5 -5
  11. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  12. package/patterns/ui-testing.md +13 -0
  13. package/reference/autogen/PATTERN_INDEX.md +35 -35
  14. package/reference/autogen/packages/assistant-core.md +33 -10
  15. package/reference/autogen/packages/assistant-runtime.md +3 -2
  16. package/reference/autogen/packages/assistant-voice-sherpa.md +1 -0
  17. package/reference/autogen/packages/assistant-voice.md +1 -0
  18. package/reference/autogen/packages/shell-web.md +15 -0
  19. package/skills/jskit/SKILL.md +20 -7
  20. package/skills/jskit/references/app-operations.md +29 -0
  21. package/skills/jskit/references/crud-operations.md +100 -0
  22. package/skills/jskit/references/pattern-index.md +35 -35
  23. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +4 -4
  24. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +5 -5
  25. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  26. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  27. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  28. package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +33 -0
  29. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
  30. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  31. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  32. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  33. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
  34. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
  35. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
@@ -175,7 +175,13 @@ committing or sending messages. An optional synchronous `transfer(text)` must
175
175
  recheck the host's empty typed draft, set it through its original setter, and
176
176
  return true; false leaves speech untouched. Taking an active partial uses the
177
177
  original capture cancellation and guarded hands-free restart, preserving the
178
- session and playback. Taking a pending review clears only that review. Hosts
178
+ session and playback. Taking a pending review clears only that review. Once an
179
+ interrupted review is resolved, stale partial words from its idle capture do not
180
+ block a fresh Talk gesture or become a new request. If capture interrupts an
181
+ endpoint commitment, only that recording's unfinished commitment is retired;
182
+ its retained words stay available for explicit Send, Edit or Discard. Late finals
183
+ and resets cannot admit or replace the retained review. Active capture, including
184
+ transcription, and pending startup, review and delivery retain their guards. Hosts
179
185
  also gate these controls while their canonical delivery has uncertain admission.
180
186
 
181
187
  For custom compositions, its optional `toolsTarget` is a DOM element belonging
@@ -381,6 +387,15 @@ models; one synthesis worker retains only the selected model. Changing models
381
387
  releases the previous worker and its native caches before loading the next, while
382
388
  recognition and the connection stay running. The first reply after a change
383
389
  includes loading time. Extra choices require disk space, not resident model RAM.
390
+ The native engine advertises a bounded 120-second first-audio allowance at each
391
+ new phrase's acquired queue slot: its unchanged load and generation requests each
392
+ have a 60-second deadline. The browser returns to its original 20-second progress
393
+ watchdog on actual PCM and after chunk completion. This allowance does not change
394
+ microphone controls, playback cancellation or the separate connection heartbeat.
395
+ Other engines omit `synthesisTimeoutMs` and retain the original 20-second policy;
396
+ optional segment metadata must be an integer above 20,000 and at most 120,000 ms.
397
+ Only the current turn's advancing segment can arm it; duplicate or stale metadata
398
+ and pongs cannot extend generation indefinitely.
384
399
  `kokoro` supplies Heart, Bella,
385
400
  Emma, Michael, Adam, George and Daniel. `kokoro-q8f16` offers all 28 English
386
401
  Kokoro voices using the exact pinned `model_q8f16.onnx` (86 MB). Its preparation
@@ -188,6 +188,8 @@ the application operator to inspect the reported rows and any affected action
188
188
  target. Do not delete a request claim or infer that an action with no result did
189
189
  nothing. Repairs require the application's offline upgrade procedure and a
190
190
  database backup; requests and startup never backfill these rows.
191
+ Retrying the same rejected message ID preserves that inspection code and its row
192
+ IDs without running inference or actions again. A retry is not a history repair.
191
193
 
192
194
  New runtime metadata uses the existing JSON fields, so this ownership move adds
193
195
  no database schema migration. Stop old assistant requests before switching
@@ -487,6 +489,12 @@ message-ID key. The database arbitrates concurrent requests across server
487
489
  processes before provider or tool execution. A reused ID with a different request
488
490
  returns a conflict. An identical retry replays the recorded answer or failure
489
491
  without calling the provider or tools again, including after a process restart.
492
+ Pre-stream `AppError` failures retain their public code and details in the same
493
+ request receipt. `ACTION_PERMISSION_DENIED` details remain private, and arbitrary
494
+ non-`AppError` diagnostics are not added to the receipt. Older failure receipts
495
+ without these optional fields replay their original message and status; missing
496
+ diagnostics are not invented or backfilled. No database schema upgrade is needed
497
+ for these optional fields in the existing response JSON.
490
498
 
491
499
  The acceptance receipt is stored before execution; completion is stored before
492
500
  sending the final stream event. If a request is still running, or its process
@@ -43,6 +43,35 @@ their own exact versions in dependency declarations; do not replace them with
43
43
  The copied files immediately belong to the application. Do not add pattern
44
44
  receipts, generator provenance, completion ledgers, or hidden operation state.
45
45
 
46
+ ## Implement before investigating internals
47
+
48
+ Use the relevant public API contract and one matching example to build the
49
+ smallest working slice, then verify its observable result. When the documented
50
+ contract answers the task, stop reading and implement it. Do not trace framework
51
+ or dependency internals to confirm documented options, routing, package
52
+ discovery, transport, or lifecycle behaviour before trying that API.
53
+
54
+ Implementation source is for a concrete failure or an API question the guide
55
+ and example cannot answer. Name that failure or question before investigating,
56
+ inspect only the relevant owner, and return to implementation and verification
57
+ as soon as it is resolved. Do not tour a dependency tree for confidence. This
58
+ applies to all features and dependencies, not only CRUD screens.
59
+
60
+ ## Discover files before reading them
61
+
62
+ Discover the file, then read it. Do not infer its location from a convention.
63
+ Use paths identified in the current tree, installed metadata, or the relevant
64
+ example. When inspecting an unfamiliar package, list its files with
65
+ `rg --files <observed-package-directory>` and select the relevant paths from
66
+ that inventory before reading or searching source. Do not try guessed source
67
+ directories. An import subpath is an `exports` alias, not necessarily a physical
68
+ path; resolve it through the installed `package.json#exports` or
69
+ `import.meta.resolve()` when following an import.
70
+
71
+ This applies to every package and task, including third-party dependencies.
72
+ Use a documented option directly when it already answers the question; source
73
+ inspection is a fallback for a concrete unresolved fact.
74
+
46
75
  ## Install and compose capabilities
47
76
 
48
77
  Install selected capabilities together at the exact versions in the matching
@@ -85,6 +85,15 @@ const bookResource = defineCrudResource({
85
85
  create: { required: true }
86
86
  }
87
87
  }
88
+ },
89
+ searchSchema: {
90
+ q: {
91
+ type: "string",
92
+ oneOf: ["title"],
93
+ filterOperator: "like",
94
+ splitBy: " ",
95
+ matchAll: true
96
+ }
88
97
  }
89
98
  });
90
99
  ```
@@ -170,6 +179,90 @@ When the same create action appears in more than one visible location, choose
170
179
  one visible action deliberately; do not relax the expected label to accept an
171
180
  unrelated fallback.
172
181
 
182
+ ## Browser tests verify validation and persistence
183
+
184
+ Exercise rejected and accepted input through the same form. Check the rendered
185
+ validation message and that rejection keeps the form open; then save an accepted
186
+ value and prove it appears in the list after reloading. Use the resource's
187
+ configured messages, the screen's `saveLabel`, and its actual browser routes.
188
+ Do not infer an `aria-invalid` attribute from the fact that validation exists:
189
+ the chosen field must actually implement that attribute contract before a test
190
+ can assert it.
191
+
192
+ For the public title-only Books variant above, using the pattern's
193
+ `saveLabel: "Save book"`, default minimum-length message, and a root public
194
+ surface:
195
+
196
+ ```ts
197
+ await page.goto("/books");
198
+ await page.getByRole("link", { name: "Add book", exact: true }).filter({ visible: true }).first().click();
199
+ await expect(page).toHaveURL(/\/books\/new$/);
200
+ await page.reload();
201
+ const title = page.getByRole("textbox", { name: "Title", exact: true });
202
+ const save = page.getByRole("button", { name: "Save book", exact: true });
203
+ await title.fill("");
204
+ await save.click();
205
+ await expect(page.getByText("Length must be at least 1 characters.", { exact: true })).toBeVisible();
206
+ await expect(page).toHaveURL(/\/books\/new$/);
207
+
208
+ await title.fill("A saved book");
209
+ await save.click();
210
+ await expect(page).toHaveURL(/\/books$/);
211
+ await expect(page.getByText("A saved book", { exact: true }).filter({ visible: true })).toBeVisible();
212
+ await page.reload();
213
+ await expect(page.getByText("A saved book", { exact: true }).filter({ visible: true })).toBeVisible();
214
+ ```
215
+
216
+ For client-side routing, a click finishing does not prove the route has changed.
217
+ Before reloading after a navigation control, wait for the destination with
218
+ `await expect(page).toHaveURL(...)`, as above. Otherwise reload can reopen the
219
+ origin page and a later form or Back locator waits on the wrong screen. Use the
220
+ actual destination URL, including required context; do not add fixed sleeps or
221
+ increase timeouts to hide this race. For a contextual Back check, wait for the
222
+ form route, reload, click Back, and assert the original list URL. Test direct
223
+ entry separately against the configured fallback.
224
+
225
+ Keep the application's existing browser configuration and isolated fixtures.
226
+ Adapt the route, labels and configured error message to that application's
227
+ contract, and retain compact, medium and expanded layout coverage. Direct API
228
+ checks additionally prove rejected input is not persisted.
229
+
230
+ ## Wire list search with the initial resource
231
+
232
+ `useCrudListScreen()` enables query search and the shared list renders a Search
233
+ box. When using that screen, configure `resource.searchSchema.q` with the
234
+ searchable fields from the start, as in the title-only Books resource above.
235
+ Showing a Search box alone does not implement storage filtering. Keep the
236
+ mapping in the resource; do not add page-local filtering or duplicate request
237
+ code. The full Books source example maps `q` to title, author and notes; narrow
238
+ it when adapting the example to fewer fields.
239
+
240
+ The browser page's route query and internal CRUD query use plain `q`. The
241
+ JSON:API HTTP transport uses `filter[q]`. Shared CRUD clients encode and decode
242
+ this automatically. Raw HTTP/API tests must send the transport key themselves:
243
+
244
+ ```ts
245
+ const headers = { Accept: "application/vnd.api+json" };
246
+ const matches = await request.get("/api/books", {
247
+ headers, params: { "filter[q]": "A saved book" }
248
+ });
249
+ expect(matches.status()).toBe(200);
250
+ expect((await matches.json()).data.some(book => book.attributes.title === "A saved book")).toBe(true);
251
+
252
+ const noMatches = await request.get("/api/books", {
253
+ headers, params: { "filter[q]": "no-matching-fixture-title" }
254
+ });
255
+ expect(noMatches.status()).toBe(200);
256
+ expect((await noMatches.json()).data).toEqual([]);
257
+ ```
258
+
259
+ Use controlled fixtures with a known matching and nonmatching value. A matching
260
+ row alone does not prove search works: an unfiltered response also includes it.
261
+ In the browser, fill Search, assert the expected visible rows and excluded rows,
262
+ then clear it and assert the list returns. Verify route/reload behaviour when
263
+ the screen synchronizes search to the route. Add these checks to the initial
264
+ create/list flow rather than discovering an unwired control at final review.
265
+
173
266
  ## Direct API tests use JSON:API documents
174
267
 
175
268
  JSKIT's CRUD screens and HTTP client apply the resource's JSON:API transport
@@ -216,6 +309,13 @@ decision. Use `CrudDeleteAction` and `useCrudDeleteAction()` through the view
216
309
  actions slot; do not rebuild their confirmation, request, invalidation, and
217
310
  navigation flow.
218
311
 
312
+ The delete action resolves its HTTP client from the resource's `apiAccess`, just
313
+ like the list, view and form helpers. For `apiAccess: "public"`, DELETE requests
314
+ skip session CSRF discovery; no `/api/session` endpoint or page-local client
315
+ wiring is required. Authenticated resources retain the configured client's CSRF
316
+ behavior. An explicit `client` override is supported and receives the same
317
+ resource-aware public request options. This does not change server authorization.
318
+
219
319
  For custom form actions, see
220
320
  [record form actions](https://mobily-enterprises.github.io/jskit-ai/guide/framework/crud-form-actions).
221
321
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.207",
3
+ "version": "0.1.208",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -5,7 +5,7 @@
5
5
  "private": true,
6
6
  "type": "module",
7
7
  "dependencies": {
8
- "@jskit-ai/kernel": "0.1.235",
8
+ "@jskit-ai/kernel": "0.1.236",
9
9
  "json-rest-schema": "^1.0.17"
10
10
  },
11
11
  "exports": {
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.1.235",
37
+ "@jskit-ai/kernel": "0.1.236",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "json-rest-schema": "^1.0.17",
@@ -42,13 +42,13 @@
42
42
  "vue": "^3.5.43",
43
43
  "vue-router": "^5.3.1",
44
44
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.1.233",
45
+ "@jskit-ai/http-runtime": "0.1.234",
46
46
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
47
47
  "@vue/devtools-api": "^8.2.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@jskit-ai/config-eslint": "0.1.232",
51
- "@jskit-ai/jskit-catalog": "0.1.260",
50
+ "@jskit-ai/config-eslint": "0.1.233",
51
+ "@jskit-ai/jskit-catalog": "0.1.261",
52
52
  "@playwright/test": "1.61.1",
53
53
  "@vitejs/plugin-vue": "^6.0.7",
54
54
  "eslint": "^10.8.0",
@@ -26,6 +26,22 @@ The normal layer split is:
26
26
  2. `repository.js`: add internal-only filter keys such as visibility scope, or call `listFilterContract.toJsonRestQuery(query)` for structured filters
27
27
  3. provider/resource registration: merge backend filter behavior into JSON REST with `createJsonRestResourceScopeOptions(resource, { searchSchema })`
28
28
 
29
+ ### Shared list and HTTP transport
30
+
31
+ `useCrudListScreen()` enables query-backed search. Configure
32
+ `resource.searchSchema.q` when composing the initial resource so its visible
33
+ Search control actually filters storage. Narrow the example's `oneOf` fields
34
+ when adapting a resource; do not drop search while retaining the control.
35
+
36
+ Names in this pattern's schemas and internal queries are plain keys such as
37
+ `q` and `archived`. Public JSON:API HTTP queries encode them as `filter[q]`
38
+ and `filter[archived]`. The browser page route may still use `?q=...`; it is
39
+ not the API transport. Shared CRUD clients perform the conversion. Direct
40
+ HTTP clients use, for example, `params: { "filter[q]": "Ada" }` against the
41
+ actual scoped API URL. Standard CRUD query validators already accept `q`
42
+ through this transport; only additional public filter keys need their own
43
+ filter validator. Verify matching and nonmatching results plus clearing Search.
44
+
29
45
  ## Decision Rules
30
46
 
31
47
  Use `search: true` on a schema field when:
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.1.235",
37
+ "@jskit-ai/kernel": "0.1.236",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "json-rest-schema": "^1.0.17",
@@ -42,15 +42,15 @@
42
42
  "vue": "^3.5.43",
43
43
  "vue-router": "^5.3.1",
44
44
  "vuetify": "^4.1.2",
45
- "@jskit-ai/http-runtime": "0.1.233",
45
+ "@jskit-ai/http-runtime": "0.1.234",
46
46
  "@mdi/js": "^7.4.47",
47
- "@jskit-ai/shell-web": "0.1.239",
47
+ "@jskit-ai/shell-web": "0.1.240",
48
48
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
49
49
  "@vue/devtools-api": "^8.2.1"
50
50
  },
51
51
  "devDependencies": {
52
- "@jskit-ai/config-eslint": "0.1.232",
53
- "@jskit-ai/jskit-catalog": "0.1.260",
52
+ "@jskit-ai/config-eslint": "0.1.233",
53
+ "@jskit-ai/jskit-catalog": "0.1.261",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "description": "App-local runtime composition and lightweight glue.",
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.1.235"
12
+ "@jskit-ai/kernel": "0.1.236"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",
@@ -18,6 +18,11 @@ Rules:
18
18
  app-owned. Do not duplicate base-URL, web-server, or storage-state logic in
19
19
  individual tests.
20
20
  - Use relative paths such as `page.goto("/home")`. The shared config owns the browser base URL.
21
+ - After clicking a client-side navigation control, wait for its destination
22
+ with `await expect(page).toHaveURL(...)` before reloading. Awaiting the click
23
+ alone does not wait for Vue Router to complete. Contextual Back tests must
24
+ reach the form before reload, then assert the original list URL; direct entry
25
+ checks the configured fallback separately. Do not replace this wait with sleeps.
21
26
  - A managed runner supplies `PLAYWRIGHT_BASE_URL`. When it is set, JSKIT does not start another app server.
22
27
  - Vibe64 supplies an authenticated context through `VIBE64_PLAYWRIGHT_STORAGE_STATE`. Treat that file as a temporary secret: do not commit it, print it, or retain it after the run.
23
28
  - Do not install a browser when the environment provides a managed browser runner.
@@ -28,6 +33,14 @@ Rules:
28
33
  Keep the expected text and persistence assertion. If repeated create controls
29
34
  perform the same action, select one visible control deliberately rather than
30
35
  accepting a different label to make a test pass.
36
+ - For CRUD validation, follow the generic CRUD guide's **Browser tests verify
37
+ validation and persistence** example: assert the visible configured error,
38
+ retained form, accepted save and reload persistence. Assert `aria-invalid`
39
+ only when the selected field implements that contract.
40
+ - Include the shared list's Search control in the initial feature flow: prove
41
+ matching rows, excluded rows and clearing the query. Raw JSON:API requests
42
+ send `filter[q]`; page-route and internal queries use plain `q`. See the CRUD
43
+ guide's **Wire list search with the initial resource** example.
31
44
 
32
45
  ## Keep startup incremental
33
46