@jskit-ai/agent-docs 0.1.298 → 0.1.300
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/guide/agent/app-extras/assistant-conversation.md +23 -6
- package/guide/agent/framework/side-effects.md +70 -0
- package/guide/agent/index.md +2 -0
- package/package.json +1 -1
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/example/AGENTS.md +6 -0
- package/patterns/minimal-foundation/example/bin/develop.js +9 -3
- package/patterns/minimal-foundation/example/package.json +3 -3
- package/patterns/shell-foundation/example/AGENTS.md +6 -0
- package/patterns/shell-foundation/example/bin/develop.js +9 -3
- package/patterns/shell-foundation/example/package.json +4 -4
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/reference/autogen/KERNEL_MAP.md +11 -0
- package/reference/autogen/PATTERN_INDEX.md +38 -38
- package/reference/autogen/packages/assistant-core.md +1 -1
- package/reference/autogen/packages/auth-provider-local-core.md +2 -0
- package/reference/autogen/packages/connectors-catalog.md +4 -0
- package/reference/autogen/packages/connectors-core.md +3 -2
- package/reference/autogen/packages/kernel.md +22 -0
- package/reference/autogen/packages/payments-core.md +1 -1
- package/reference/autogen/packages/shell-web.md +16 -0
- package/reference/autogen/packages/workspaces-core.md +4 -0
- package/skills/jskit/SKILL.md +1 -0
- package/skills/jskit/references/pattern-index.md +38 -38
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +6 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/bin/develop.js +9 -3
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +3 -3
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +6 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/bin/develop.js +9 -3
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +4 -4
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +3 -3
- package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/api-key-connection/example/connections.js +4 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/connectors/firebase-messaging/example/server/notifications.js +4 -1
- package/skills/jskit/references/patterns/crud/crud-feature-package/example/packages/books/package.json +3 -3
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +3 -3
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +6 -6
- package/skills/jskit/references/side-effects.md +65 -0
|
@@ -1274,8 +1274,17 @@ supplied binding never blocks on message sending or unknown delivery.
|
|
|
1274
1274
|
An active goal flashes red; a paused goal stays orange. `elapsedSeconds` is the
|
|
1275
1275
|
accumulated **active running time**, and `sampledAt` is its sample time in epoch
|
|
1276
1276
|
**milliseconds**. The UI adds time only while active. Omit elapsed time if the
|
|
1277
|
-
backend cannot supply it truthfully.
|
|
1278
|
-
|
|
1277
|
+
backend cannot supply it truthfully. Only an active goal's chip shows its clock;
|
|
1278
|
+
a `paused`, `blocked`, `usageLimited` or `budgetLimited` goal's chip shows its
|
|
1279
|
+
status label (for example "Goal paused") instead, and its details keep the
|
|
1280
|
+
elapsed time. A `usageLimited` goal is waiting at its AI account's limit: its
|
|
1281
|
+
label names the limit and, from an optional `resetsAt` (epoch **seconds**), when it
|
|
1282
|
+
resets ("Waiting: AI usage limit, resets at 05:00", with the day when the reset is
|
|
1283
|
+
not today); with `outOfCredit: true` it reads "Out of credit: top up or switch AI
|
|
1284
|
+
account". Its details say the goal does not continue by itself and offer Resume
|
|
1285
|
+
and Cancel, and Pause when `pauseBlocked` is set. In a narrow conversation pane
|
|
1286
|
+
the clock hides and a long label ends in an ellipsis; the light stays visible
|
|
1287
|
+
and details retain the time and full label. Reduced motion uses
|
|
1279
1288
|
a steady red light. Pause follows the command's declared behavior:
|
|
1280
1289
|
`pauseInterruptsTurn: true` means it stops the current turn and keeps the goal;
|
|
1281
1290
|
otherwise it prevents further automatic turns while Stop interrupts current work.
|
|
@@ -1286,10 +1295,18 @@ adapter. For a low-level adapter, omit `goal` or use `enabled: false` when goals
|
|
|
1286
1295
|
are unsupported. The supplied binding hides unsupported controls automatically.
|
|
1287
1296
|
|
|
1288
1297
|
Claude Pause retains the objective and native identity in the existing conversation
|
|
1289
|
-
binding before interrupting execution.
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1298
|
+
binding before interrupting execution. It sends nothing to Claude, so it works when
|
|
1299
|
+
the account is out of credit. When another message starts a process, the
|
|
1300
|
+
native owner clears the restored checker with Claude's local command (no model
|
|
1301
|
+
request) and requires its exact successful receipt before admitting user input.
|
|
1302
|
+
Ordinary messages and process restart keep the goal paused. Only that retained
|
|
1303
|
+
Pause reads `paused`: a Claude goal whose turn ended any other way (an interrupt,
|
|
1304
|
+
a process restart) keeps its checker armed, so the next turn continues it and it
|
|
1305
|
+
reads `active`. When Claude's own history ends the goal's turn with its usage-limit
|
|
1306
|
+
message, the goal reads `usageLimited` with that message's `resetsAt`; its
|
|
1307
|
+
credit-balance message reads `usageLimited` with `outOfCredit: true`. A later
|
|
1308
|
+
answer returns it to `active`. Hosts that restart goals must not resume a waiting
|
|
1309
|
+
goal: Resume after the reset stays the person's choice. Explicit Resume reinstalls the objective;
|
|
1293
1310
|
Cancel removes the retained goal. A failed native clear leaves the new message
|
|
1294
1311
|
unsent and retains Pause for recovery. Hosts supplying their own Claude store must
|
|
1295
1312
|
round-trip the optional `pausedGoal` field with the existing conversation state.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/framework/side-effects.md`. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
title: Side effects and test copies
|
|
5
|
+
description: Follow SIDE_EFFECTS so a test copy never sends real email, messages or payments.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Side effects and test copies
|
|
9
|
+
|
|
10
|
+
An application reads one environment variable, `SIDE_EFFECTS`:
|
|
11
|
+
|
|
12
|
+
- `live`: the application acts for real.
|
|
13
|
+
- Anything else, including unset, means `simulate`: the copy is a **test
|
|
14
|
+
copy**. It sends no real email, SMS, push notification, payment or
|
|
15
|
+
third-party webhook. It simulates each one, writes it to the log, and every
|
|
16
|
+
page shows a small "Test copy" banner.
|
|
17
|
+
|
|
18
|
+
Only the copy that serves real people sets `SIDE_EFFECTS=live`. Development,
|
|
19
|
+
previews, tests and staging leave it unset. The name has no platform prefix on
|
|
20
|
+
purpose: any host can set it. The host sets it; a request never chooses it.
|
|
21
|
+
|
|
22
|
+
## What JSKIT does
|
|
23
|
+
|
|
24
|
+
- Every HTML page the server sends, and every page the Vite development server
|
|
25
|
+
sends, names the setting in a `jskit-side-effects` meta tag. The shell
|
|
26
|
+
(`@jskit-ai/shell-web`) shows the "Test copy" banner from it, with no
|
|
27
|
+
application code. The banner never takes a click or a tap.
|
|
28
|
+
- The server logs once at startup whether it acts for real. A production server
|
|
29
|
+
that simulates logs a warning.
|
|
30
|
+
- Local-password emails (confirmation and password reset) with an installed
|
|
31
|
+
sender are logged, not sent.
|
|
32
|
+
- Workspace invitations with a provided mailer are logged. The invitation
|
|
33
|
+
answers `status: "simulated"`, and the inviter copies the link.
|
|
34
|
+
- Accounts-hub email is logged, not sent.
|
|
35
|
+
- Connector operations that act on the world (send an email or message, post,
|
|
36
|
+
book or cancel, charge, invoice, trigger another system's events) are marked
|
|
37
|
+
`sideEffect`. They are checked like real ones, then logged, and `invoke`
|
|
38
|
+
answers `{ simulated: true }`. Reads run for real. `createConnectorsFeature`
|
|
39
|
+
reads the setting itself; give `createConnectionService` and
|
|
40
|
+
`createPaymentCheckoutService` `sideEffects: resolveSideEffectsMode(env)`.
|
|
41
|
+
- Payment checkout and the billing portal in the live payment environment are
|
|
42
|
+
refused with `payment_test_copy`; the sandbox environment works either way.
|
|
43
|
+
|
|
44
|
+
Supabase sends its own authentication emails from Supabase's servers; JSKIT
|
|
45
|
+
cannot simulate them. Point a test copy at a separate Supabase project.
|
|
46
|
+
|
|
47
|
+
## The application's own side effects
|
|
48
|
+
|
|
49
|
+
Code the application writes that reaches the world follows the same rule.
|
|
50
|
+
Decide at the point of the effect: log what would happen and answer as if it
|
|
51
|
+
were done.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { sideEffectsAreLive } from "@jskit-ai/kernel/shared/support/sideEffects";
|
|
55
|
+
import { logSimulatedSideEffect } from "@jskit-ai/kernel/server/runtime/sideEffects";
|
|
56
|
+
|
|
57
|
+
async function sendReminder({ env, logger, mailer }, message) {
|
|
58
|
+
if (!sideEffectsAreLive(env)) {
|
|
59
|
+
logSimulatedSideEffect(logger, { kind: "email", summary: `the reminder to ${message.to}`, details: { to: message.to } });
|
|
60
|
+
return { simulated: true };
|
|
61
|
+
}
|
|
62
|
+
return mailer.send(message);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- Read `env` from the `runtime.env` capability, never a request.
|
|
67
|
+
- A configured driver or key (`EMAIL_DRIVER=ses`, a live payment key) does not
|
|
68
|
+
make a test copy live; only `SIDE_EFFECTS=live` does.
|
|
69
|
+
- Test both modes. A test that proves a real sender against a fake transport
|
|
70
|
+
sets `SIDE_EFFECTS: "live"` in its environment.
|
package/guide/agent/index.md
CHANGED
|
@@ -40,6 +40,8 @@ apply or adapt. It does not prescribe an agent host or project orchestrator.
|
|
|
40
40
|
- [Sanity checks](/guide/framework/sanity-checks): JSKIT's own pitfalls, and
|
|
41
41
|
the JSKIT calls that check data access, tests, a refactor, a screen or a
|
|
42
42
|
deployment before calling it done
|
|
43
|
+
- [Side effects and test copies](/guide/framework/side-effects): read before
|
|
44
|
+
code that sends email, messages or payments, or calls another system
|
|
43
45
|
- [Translating a JSKIT app](/guide/framework/translation)
|
|
44
46
|
- [Complete source pattern library](/patterns/)
|
|
45
47
|
|
package/package.json
CHANGED
|
@@ -26,6 +26,12 @@ factory or client provider), read the client features guide at
|
|
|
26
26
|
Simple, direct code comes first: never add a layer, wrapper, registry or tiny
|
|
27
27
|
module only to satisfy a rule. `npm run lint` checks its rules too.
|
|
28
28
|
|
|
29
|
+
Before writing code that sends email, SMS, push notifications or payments, or
|
|
30
|
+
calls another system, read the side effects guide at
|
|
31
|
+
`https://mobily-enterprises.github.io/jskit-ai/guide/framework/side-effects`.
|
|
32
|
+
Unless `SIDE_EFFECTS` is exactly `live`, the app is a test copy: it simulates
|
|
33
|
+
them and logs them instead.
|
|
34
|
+
|
|
29
35
|
Before implementation, read the narrow relevant source pattern completely. If
|
|
30
36
|
its owner package is installed, prefer that version-matched package-owned copy.
|
|
31
37
|
Do not install an unrelated runtime package only to read documentation.
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
2
5
|
import { startServer } from "../server.js";
|
|
3
6
|
import { DEFAULT_PORT } from "../server/lib/runtimeEnv.js";
|
|
4
7
|
|
|
@@ -43,10 +46,13 @@ process.once("SIGTERM", () => requestShutdown("SIGTERM"));
|
|
|
43
46
|
try {
|
|
44
47
|
apiServer = await startServer({ host: LOOPBACK_HOST, port: 0 });
|
|
45
48
|
const apiPort = /** @type {import("node:net").AddressInfo} */ (apiServer.server.address()).port;
|
|
46
|
-
|
|
49
|
+
// Vite runs directly under node rather than through `npm run dev`: npm exits on SIGTERM without stopping
|
|
50
|
+
// the script it started, which left Vite serving the port after this launcher stopped.
|
|
51
|
+
const vitePackageFile = createRequire(import.meta.url).resolve("vite/package.json");
|
|
52
|
+
const viteCli = path.join(path.dirname(vitePackageFile), JSON.parse(readFileSync(vitePackageFile, "utf8")).bin.vite);
|
|
47
53
|
viteProcess = spawn(
|
|
48
|
-
|
|
49
|
-
[
|
|
54
|
+
process.execPath,
|
|
55
|
+
[viteCli, "--host", previewHost, "--port", String(previewPort), "--strictPort"],
|
|
50
56
|
{
|
|
51
57
|
cwd: process.cwd(),
|
|
52
58
|
env: {
|
|
@@ -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.
|
|
37
|
+
"@jskit-ai/kernel": "0.2.20",
|
|
38
38
|
"@jskit-ai/material-web": "0.1.5",
|
|
39
39
|
"@tanstack/vue-query": "^5.101.0",
|
|
40
40
|
"fastify": "^5.12.7",
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"vue": "^3.5.44",
|
|
45
45
|
"vue-router": "^5.3.1",
|
|
46
46
|
"vuetify": "^4.3.0",
|
|
47
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
47
|
+
"@jskit-ai/http-runtime": "0.2.20",
|
|
48
48
|
"@fastify/fast-json-stringify-compiler": "^5.1.0",
|
|
49
49
|
"@vue/devtools-api": "^8.2.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@jskit-ai/config-eslint": "0.1.250",
|
|
53
|
-
"@jskit-ai/jskit-catalog": "0.2.
|
|
53
|
+
"@jskit-ai/jskit-catalog": "0.2.52",
|
|
54
54
|
"@playwright/test": "1.61.1",
|
|
55
55
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
56
56
|
"eslint": "^10.8.0",
|
|
@@ -26,6 +26,12 @@ factory or client provider), read the client features guide at
|
|
|
26
26
|
Simple, direct code comes first: never add a layer, wrapper, registry or tiny
|
|
27
27
|
module only to satisfy a rule. `npm run lint` checks its rules too.
|
|
28
28
|
|
|
29
|
+
Before writing code that sends email, SMS, push notifications or payments, or
|
|
30
|
+
calls another system, read the side effects guide at
|
|
31
|
+
`https://mobily-enterprises.github.io/jskit-ai/guide/framework/side-effects`.
|
|
32
|
+
Unless `SIDE_EFFECTS` is exactly `live`, the app is a test copy: it simulates
|
|
33
|
+
them and logs them instead.
|
|
34
|
+
|
|
29
35
|
Before implementation, read the narrow relevant source pattern completely. If
|
|
30
36
|
its owner package is installed, prefer that version-matched package-owned copy.
|
|
31
37
|
Do not install an unrelated runtime package only to read documentation.
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
2
5
|
import { startServer } from "../server.js";
|
|
3
6
|
import { DEFAULT_PORT } from "../server/lib/runtimeEnv.js";
|
|
4
7
|
|
|
@@ -43,10 +46,13 @@ process.once("SIGTERM", () => requestShutdown("SIGTERM"));
|
|
|
43
46
|
try {
|
|
44
47
|
apiServer = await startServer({ host: LOOPBACK_HOST, port: 0 });
|
|
45
48
|
const apiPort = /** @type {import("node:net").AddressInfo} */ (apiServer.server.address()).port;
|
|
46
|
-
|
|
49
|
+
// Vite runs directly under node rather than through `npm run dev`: npm exits on SIGTERM without stopping
|
|
50
|
+
// the script it started, which left Vite serving the port after this launcher stopped.
|
|
51
|
+
const vitePackageFile = createRequire(import.meta.url).resolve("vite/package.json");
|
|
52
|
+
const viteCli = path.join(path.dirname(vitePackageFile), JSON.parse(readFileSync(vitePackageFile, "utf8")).bin.vite);
|
|
47
53
|
viteProcess = spawn(
|
|
48
|
-
|
|
49
|
-
[
|
|
54
|
+
process.execPath,
|
|
55
|
+
[viteCli, "--host", previewHost, "--port", String(previewPort), "--strictPort"],
|
|
50
56
|
{
|
|
51
57
|
cwd: process.cwd(),
|
|
52
58
|
env: {
|
|
@@ -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.
|
|
37
|
+
"@jskit-ai/kernel": "0.2.20",
|
|
38
38
|
"@jskit-ai/material-web": "0.1.5",
|
|
39
39
|
"@tanstack/vue-query": "^5.101.0",
|
|
40
40
|
"fastify": "^5.12.7",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"vue": "^3.5.44",
|
|
45
45
|
"vue-router": "^5.3.1",
|
|
46
46
|
"vuetify": "^4.3.0",
|
|
47
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
48
|
-
"@jskit-ai/shell-web": "0.2.
|
|
47
|
+
"@jskit-ai/http-runtime": "0.2.20",
|
|
48
|
+
"@jskit-ai/shell-web": "0.2.26",
|
|
49
49
|
"@fastify/fast-json-stringify-compiler": "^5.1.0",
|
|
50
50
|
"@vue/devtools-api": "^8.2.1"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@jskit-ai/config-eslint": "0.1.250",
|
|
54
|
-
"@jskit-ai/jskit-catalog": "0.2.
|
|
54
|
+
"@jskit-ai/jskit-catalog": "0.2.52",
|
|
55
55
|
"@playwright/test": "1.61.1",
|
|
56
56
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
57
57
|
"eslint": "^10.8.0",
|
|
@@ -170,6 +170,17 @@ Exports
|
|
|
170
170
|
Local functions
|
|
171
171
|
- `normalizePlacementRenderers(value = {})`
|
|
172
172
|
|
|
173
|
+
### `support/sideEffects.js`
|
|
174
|
+
Exports
|
|
175
|
+
- `SIDE_EFFECTS_ENV_KEY`
|
|
176
|
+
- `SIDE_EFFECTS_LIVE`
|
|
177
|
+
- `SIDE_EFFECTS_META_NAME`
|
|
178
|
+
- `SIDE_EFFECTS_SIMULATE`
|
|
179
|
+
- `pageSideEffectsMode(documentObject = globalThis.document)`
|
|
180
|
+
- `resolveSideEffectsMode(env)`
|
|
181
|
+
- `sideEffectsAreLive(env)`
|
|
182
|
+
- `withSideEffectsMeta(html, mode)`
|
|
183
|
+
|
|
173
184
|
### `support/silentLogger.js`
|
|
174
185
|
Exports
|
|
175
186
|
- `silentLogger`
|