@jskit-ai/agent-docs 0.1.265 → 0.1.267

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 (36) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +18 -4
  2. package/guide/agent/app-setup/authentication.md +8 -0
  3. package/guide/agent/app-setup/multi-homing.md +5 -3
  4. package/guide/agent/framework/translation.md +56 -10
  5. package/guide/agent/framework/ui-operations.md +11 -12
  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/shell-foundation/example/package.json +5 -5
  10. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  11. package/reference/autogen/PATTERN_INDEX.md +37 -37
  12. package/reference/autogen/packages/assistant-core.md +5 -3
  13. package/reference/autogen/packages/auth-core.md +4 -1
  14. package/reference/autogen/packages/oidc-issuer-core.md +23 -4
  15. package/reference/autogen/packages/resource-crud-core.md +2 -1
  16. package/reference/autogen/packages/users-web.md +26 -0
  17. package/reference/autogen/packages/workspaces-core.md +2 -1
  18. package/reference/autogen/tooling/config-eslint.md +8 -0
  19. package/skills/jskit/references/multi-homing.md +5 -3
  20. package/skills/jskit/references/pattern-index.md +37 -37
  21. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +4 -4
  22. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +5 -5
  23. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  24. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
  25. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  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/example/packages/books/package.json +2 -2
  29. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  30. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  31. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  32. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
  33. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
  34. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
  35. package/skills/jskit/references/translation.md +56 -10
  36. package/skills/jskit/references/ui-operations.md +11 -12
@@ -486,7 +486,13 @@ fact cannot repair an older saved uncertain request or grant actor access.
486
486
  When `draftStorage` restores a page, known failed deliveries remain failed and
487
487
  accepted receipts remain accepted. Saved uncertain, pending or unrecognised
488
488
  delivery states stay uncertain with the exact original ID, payload and draft;
489
- closing a page during HTTP is not proof of rejection. Only the transient
489
+ closing a page during HTTP is not proof of rejection. A draft message that was
490
+ never dispatched (one deferred behind a working turn, for example) can no longer
491
+ be sent once its conversation is released: its words return to the saved draft,
492
+ ahead of anything typed after it, and no delivery is saved for it. A draft
493
+ submission that settles undelivered without leaving a delivery record (refused
494
+ before one was recorded) gives its words back to the composer, as a steer
495
+ already did. Only the transient
490
496
  inspection flag is reset so **Retry** is reachable again. A matching
491
497
  canonical row marked `receipt: false` cannot settle delivery. Unknown inspection
492
498
  never retries; an actual accepted receipt is required to acknowledge the request.
@@ -3425,8 +3431,13 @@ at shutdown each native owner stops the processes this server runs, since none
3425
3431
  outlives the server that started it, and saved conversations are not reopened to
3426
3432
  be stopped. Claude stops its sessions together, leaves an idle chat untouched
3427
3433
  (nothing to stop, nothing written) and reports a session that cannot stop by ID
3428
- in `failed` while the others still stop. It stops them even when its
3429
- account-query cleanup fails; that failure is still the call's result. Once
3434
+ in `failed` while the others still stop. A turn still running when its process
3435
+ stops is not ended at shutdown: its saved record keeps the execution and the
3436
+ active turn, as after a crash, so the next server's recovery proves that exit,
3437
+ interrupts the turn and runs the host's interrupted checkpoint. A slow host
3438
+ checkpoint, such as a Git snapshot, never holds up shutdown. Claude stops its
3439
+ sessions even when its account-query cleanup fails; that failure is still the
3440
+ call's result. Once
3430
3441
  shutdown has invalidated the owner, a later session close only releases retained
3431
3442
  work. Failed cleanup can be retried after runtime closure. This is not a browser
3432
3443
  operation or a way to submit a caller-selected native owner.
@@ -3453,7 +3464,10 @@ owners. The host synchronously supplies `{ engine, native, context, sessions,
3453
3464
  options }` for operation `"reconcileSessions"`. The application retains its
3454
3465
  session inventory, authorization and storage codecs. Codex retains its original
3455
3466
  concurrent recovery and prune coordination; OpenCode retains per-session failure
3456
- results and continues; Claude retains sequential, fail-fast restoration.
3467
+ results and continues; Claude retains sequential, fail-fast restoration. Like a
3468
+ session close, Claude reconciliation reopens only the saved custody an earlier
3469
+ server left, so a verified stop can retire it; idle saved conversations stay
3470
+ closed and unwritten.
3457
3471
 
3458
3472
  `runtime.unsubscribeNativeSessions({ context, sessions })` supplies startup
3459
3473
  subscription reset for Codex through operation `"unsubscribeSessions"` and a
@@ -236,5 +236,13 @@ A managed host instead supplies authenticated state through
236
236
  file as a temporary secret, use relative URLs, and do not call the localhost
237
237
  login helper or start a duplicate server.
238
238
 
239
+ The login state belongs to the host, which already fits it to the tested site.
240
+ Pass `process.env.PLAYWRIGHT_STORAGE_STATE` unchanged to `use.storageState`:
241
+ the config never reads, parses or edits that file or its `cookies` or
242
+ `origins`. `jskit/playwright-login-unchanged` reports a config that does, and
243
+ the host refuses to run its tests. If tests run signed out with the unchanged
244
+ state, that is a host defect: report it with the evidence; do not work around
245
+ it.
246
+
239
247
  Never commit provider secrets, copy auth repositories into the app, or add an
240
248
  installation questionnaire, mutation record, or setup receipt.
@@ -198,9 +198,11 @@ const InviteMailerProvider = defineProvider({
198
198
 
199
199
  The optional `workspaces.invite-email-template` capability is a function that
200
200
  renders that inner message; it receives `{ email, inviteUrl, workspace, inviter,
201
- roleSid, roleLabel, expiresAt, locale, t }` (`roleLabel` only when configured).
202
- Provide it from an application feature, as for the mailer; without it the
203
- built-in template renders the message. Invitation links use `/invite/:token` unless
201
+ roleSid, roleLabel, expiresAt, locale, timeZone, t }` (`roleLabel` only when
202
+ configured; `locale` and `timeZone` are the recipient's saved settings, else
203
+ `en` and `UTC`). Provide it from an application feature, as for the mailer;
204
+ without it the built-in template renders the message, with the expiry written
205
+ as a date in that locale and time zone. Invitation links use `/invite/:token` unless
204
206
  `workspaceInvitations.invitePath` names another path. The
205
207
  `workspaces/workspace-server` pattern includes an example renderer.
206
208
 
@@ -135,6 +135,9 @@ the English `message`:
135
135
  values and render from `jskit.errors.<code>`.
136
136
  - Validation errors keep `details.fieldErrors` (English) and add
137
137
  `details.fieldErrorCodes: { field: { code, params } }`.
138
+ - A schema's field `messages` may give a message as a string or as
139
+ `{ code, message, params }` (see below); the coded form travels as that
140
+ field's code.
138
141
  - Where one code covers several different sentences (for example
139
142
  `connector_input_invalid`), the response also has `messageCode` and
140
143
  `messageParams` for the exact sentence.
@@ -164,6 +167,33 @@ throw new AppError(409, "That slug is taken.", { code: "app.project.slugTaken" }
164
167
  // en: { jskit: { app: { project: { slugTaken: "That slug is taken." } } } }
165
168
  ```
166
169
 
170
+ ### Field messages in schemas
171
+
172
+ A field's `messages` in a resource or command schema map a validation keyword
173
+ (or `default`) to the message shown when that check fails. A plain string is
174
+ sent as written and is never translated. To make it translatable, give the
175
+ coded form instead: the code, the English (exactly the catalog's English with
176
+ the params filled in) and any params.
177
+
178
+ ```js
179
+ const NAME_MESSAGES = Object.freeze({
180
+ required: { code: "app.project.nameRequired", message: "Name is required." },
181
+ maxLength: { code: "app.project.nameTooLong", message: "Use at most 80 characters.", params: { max: 80 } }
182
+ });
183
+
184
+ const schema = {
185
+ name: { type: "string", required: true, maxLength: 80, messages: NAME_MESSAGES }
186
+ };
187
+ // en: { jskit: { app: { project: {
188
+ // nameRequired: "Name is required.", nameTooLong: "Use at most {max} characters." } } } }
189
+ ```
190
+
191
+ The server still sends the English in `details.fieldErrors` and adds the code
192
+ and params to `details.fieldErrorCodes`, so an untranslated app shows exactly
193
+ the English it showed before. The failed check's own params (for example
194
+ `limit`) are sent too, and the coded entry's `params` win over them. JSKIT's
195
+ own schemas use this form, which is why their field messages translate.
196
+
167
197
  ## Placement labels
168
198
 
169
199
  A placement `label` or `title` may be a message key, `{ key, fallback, params }`
@@ -186,12 +216,21 @@ App code can use the same helpers from `useJskitText()`: `formatDate`,
186
216
 
187
217
  ## Emails
188
218
 
189
- The workspace invitation email is rendered in the recipient's saved
190
- `user_settings.locale` when the recipient already has an account, and in English
191
- otherwise. The server reads the same `src/locales/<code>/jskit.json` files the
192
- client uses, so translating `jskit.workspaces.inviteEmail.*` there translates
193
- the email too. It looks for the exact locale (`it-IT`), then the language
194
- (`it`), then English. The app's invite mailer also receives the chosen `locale`.
219
+ The server reads the same `src/locales/<code>/jskit.json` files the client
220
+ uses, so translating a JSKIT email's keys there translates the email too. It
221
+ looks for the exact locale (`it-IT`), then the language (`it`), then English.
222
+
223
+ - The workspace invitation email (`jskit.workspaces.inviteEmail.*`) is rendered
224
+ in the recipient's saved `user_settings.locale` when the recipient already
225
+ has an account, and in English otherwise. Its expiry date is written with
226
+ `Intl` in that locale and the recipient's saved time zone (UTC otherwise).
227
+ The app's invite mailer also receives the chosen `locale`.
228
+ - JSKIT's accounts hub (`@jskit-ai/oidc-issuer-core`) sends the account
229
+ confirmation, password recovery, Google link and test emails itself, from
230
+ `jskit.accounts.*`. Hub accounts keep no saved language, so each email
231
+ follows the request that sent it: the app's `ui_locales`, then the browser's
232
+ `Accept-Language`, then English. The hub reads the hub project's
233
+ `src/locales/<code>/jskit.json`; its README has the details.
195
234
 
196
235
  Server code that renders JSKIT-style messages itself uses the same lookup:
197
236
 
@@ -202,9 +241,10 @@ const i18n = createServerI18n({ appRoot, catalogs: [english] });
202
241
  i18n.t("it-IT", "jskit.workspaces.inviteEmail.subject", { workspace: "Acme" });
203
242
  ```
204
243
 
205
- Password-reset emails are sent by the app's own recovery sender (local
206
- accounts) or by the identity provider (Supabase, OIDC), so their language is
207
- the app's or the provider's choice.
244
+ An app that signs in through JSKIT's accounts hub gets its password-reset
245
+ email from the hub, translated as above. With another identity provider
246
+ (Supabase, a third-party OIDC issuer) the provider sends it, so its language is
247
+ the provider's choice.
208
248
 
209
249
  ## What the app translates itself
210
250
 
@@ -223,6 +263,12 @@ JSKIT's catalogs cover JSKIT's own code. These texts belong to the app:
223
263
 
224
264
  Each JSKIT package with a catalog has a test that compiles every message with
225
265
  vue-i18n, requires every key and dotted code its source uses to exist, rejects
226
- unused keys and refuses a key another package already defines. ESLint's
266
+ unused keys and refuses a key another package already defines. It also requires
267
+ an undotted code sent with fixed English to have its `jskit.errors.<code>`
268
+ entry (or a `messageCode`), and a literal `{ code, message }` to send its
269
+ catalog's English. A browser test exports a pseudo-locale with
270
+ `jskit i18n export`, switches a fixture app to it, and fails on any English left
271
+ on the sign-in, registration, password reset, sign-out, profile menu, account
272
+ settings and error screens. ESLint's
227
273
  `@intlify/vue-i18n/no-raw-text` rule rejects untranslated template text in JSKIT
228
274
  client code.
@@ -26,7 +26,7 @@ template or relative string raw to Vue Router `to`.
26
26
  ## Back navigation
27
27
 
28
28
  Check every page for a visible Back button that returns to the previous page.
29
- Omit it only when returning makes no product sense, such as the app's root
29
+ Omit it only when returning makes no product sense, such as the root
30
30
  landing page or a completed flow that must not reopen an expired step. State
31
31
  the reason in the implementation or review result; a drawer link is not Back.
32
32
 
@@ -70,7 +70,6 @@ hash, pattern receipt, or tool-owned source declaration.
70
70
  - Keep screens phone/task-first with drawer-independent primary actions.
71
71
  - Use direct surfaces instead of needless nested cards.
72
72
  - Render all meaningful loading, empty, error, permission, and retry states.
73
- - Follow the Material 3 contract for targets, loading and failure feedback.
74
73
  - Extend shared CRUD screens through public slots and composables.
75
74
  - Keep row actions and filters near the screen unless the framework owns them.
76
75
  - Import neutral request and CRUD UI APIs from `@jskit-ai/http-web`. Install
@@ -87,25 +86,25 @@ when another navigation affordance remains.
87
86
  The drawer omits the app bar's surface label. Open and rail icons share a
88
87
  centreline. It uses a 12px outer item inset; `navigationItemSpacing` controls
89
88
  icon/label and label/end gaps. The normal 80px rail centres 48px targets;
90
- `railWidth` and `drawerWidth` are public density/width controls. Do not override
91
- the shell's private implementation CSS.
92
-
93
- Restyle shell chrome only through the `--shell-*` tokens in the
94
- `shell/application-shell` pattern.
89
+ `railWidth` and `drawerWidth` are public density/width controls. Restyle shell
90
+ chrome only through the `--shell-*` tokens in the `shell/application-shell`
91
+ pattern, never its private CSS.
95
92
 
96
93
  ## Browser verification
97
94
 
98
95
  Exercise user-facing changes with Playwright at compact, medium, and expanded
99
96
  widths. Check overflow, clipped text, duplicate navigation, route placement,
100
97
  actions, skeleton replacement, error feedback, and target sizes. Use relative
101
- URLs. When `PLAYWRIGHT_BASE_URL` is provided, start no duplicate server.
102
- For Back, enter the same page from two different origins and verify each
103
- returns to its own origin with context intact. Also check direct entry, reload,
104
- browser back/forward, fixed-destination exceptions, and any unsaved-change guard.
98
+ URLs. With `PLAYWRIGHT_BASE_URL` set, start no duplicate server. For Back,
99
+ enter the same page from two origins and verify each returns to its own origin
100
+ with context intact. Also check direct entry, reload, browser back/forward,
101
+ fixed-destination exceptions, and any unsaved-change guard.
105
102
 
106
103
  A managed host owns its browser and may provide
107
104
  `PLAYWRIGHT_STORAGE_STATE`; never print or commit that value, bypass it,
108
- or install another browser. For explicitly enabled direct-local auth, use
105
+ or install another browser. If tests run signed out despite that state, report
106
+ the host defect with evidence and stop; never rewrite its cookies or add a login.
107
+ For explicitly enabled direct-local auth, use
109
108
  `loginAsExistingUser()` from `@jskit-ai/auth-web/test/playwright`; never expose
110
109
  the exchange secret to browser code, URLs, or client environment.
111
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.265",
3
+ "version": "0.1.267",
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.2.5",
8
+ "@jskit-ai/kernel": "0.2.6",
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.2.5",
37
+ "@jskit-ai/kernel": "0.2.6",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "pino": "^10.3.1",
@@ -43,13 +43,13 @@
43
43
  "vue": "^3.5.44",
44
44
  "vue-router": "^5.3.1",
45
45
  "vuetify": "^4.1.2",
46
- "@jskit-ai/http-runtime": "0.2.5",
46
+ "@jskit-ai/http-runtime": "0.2.6",
47
47
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
48
48
  "@vue/devtools-api": "^8.2.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@jskit-ai/config-eslint": "0.1.244",
52
- "@jskit-ai/jskit-catalog": "0.2.17",
51
+ "@jskit-ai/config-eslint": "0.1.245",
52
+ "@jskit-ai/jskit-catalog": "0.2.19",
53
53
  "@playwright/test": "1.61.1",
54
54
  "@vitejs/plugin-vue": "^6.0.7",
55
55
  "eslint": "^10.8.0",
@@ -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.2.5",
37
+ "@jskit-ai/kernel": "0.2.6",
38
38
  "@tanstack/vue-query": "^5.101.0",
39
39
  "fastify": "^5.8.5",
40
40
  "pino": "^10.3.1",
@@ -43,15 +43,15 @@
43
43
  "vue": "^3.5.44",
44
44
  "vue-router": "^5.3.1",
45
45
  "vuetify": "^4.1.2",
46
- "@jskit-ai/http-runtime": "0.2.5",
46
+ "@jskit-ai/http-runtime": "0.2.6",
47
47
  "@mdi/js": "^7.4.47",
48
- "@jskit-ai/shell-web": "0.2.6",
48
+ "@jskit-ai/shell-web": "0.2.7",
49
49
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
50
50
  "@vue/devtools-api": "^8.2.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@jskit-ai/config-eslint": "0.1.244",
54
- "@jskit-ai/jskit-catalog": "0.2.17",
53
+ "@jskit-ai/config-eslint": "0.1.245",
54
+ "@jskit-ai/jskit-catalog": "0.2.19",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "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.2.5"
12
+ "@jskit-ai/kernel": "0.2.6"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",