@happyvertical/smrt-playbooks 0.44.0 → 0.45.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/AGENTS.md +93 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +246 -3
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/preflight-types.d.ts +276 -0
- package/dist/preflight-types.js +0 -0
- package/dist/smrt-knowledge.json +127 -6
- package/package.json +6 -6
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { CapabilityClassification } from '@happyvertical/smrt-types';
|
|
2
|
+
import { CapabilityDeclaration } from '@happyvertical/smrt-types';
|
|
3
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Static layers a browser-plane step is evaluated against.
|
|
7
|
+
*
|
|
8
|
+
* Everything here is a build-time or configuration fact. The generated
|
|
9
|
+
* `authMiddleware` is deliberately absent: it is request-bound, returns a
|
|
10
|
+
* `Response` rather than a boolean, and may consult session stores, rate-limit,
|
|
11
|
+
* or audit — so preflight never invokes it, synthetically or otherwise, and
|
|
12
|
+
* `appAuthConfigured` is the only thing it is allowed to know about it.
|
|
13
|
+
*
|
|
14
|
+
* An `authPredicate` seam (option 1 in #2590) can later be added as an optional
|
|
15
|
+
* member here, turning the `app-auth` layer's `unknown` into a real verdict
|
|
16
|
+
* without changing the report contract.
|
|
17
|
+
*/
|
|
18
|
+
export declare interface BrowserPreflightLayerSource {
|
|
19
|
+
/** `isApiActionEnabled` for the referenced model operation. */
|
|
20
|
+
isActionExposed(model: string, action: string): boolean;
|
|
21
|
+
/** `isRoutePublic` for the HTTP method the action maps to. */
|
|
22
|
+
isRoutePublic(model: string, action: string): boolean;
|
|
23
|
+
/** Field-level read-permission slugs the step's model declares. */
|
|
24
|
+
requiredFieldPermissions(model: string, action: string): readonly string[];
|
|
25
|
+
/** Whether an app-level auth middleware is wired. Never invoked. */
|
|
26
|
+
appAuthConfigured: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export declare interface BrowserPreflightOptions {
|
|
30
|
+
layers: BrowserPreflightLayerSource;
|
|
31
|
+
/**
|
|
32
|
+
* The caller's published permission slugs, when the host publishes them.
|
|
33
|
+
* `null`/absent makes the field layer report `unknown` rather than guess.
|
|
34
|
+
*/
|
|
35
|
+
permissions?: Iterable<string> | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A partial override from any layer above the code default. There is no
|
|
40
|
+
* `steps` key, and supplying one is rejected rather than ignored.
|
|
41
|
+
*/
|
|
42
|
+
declare interface PlaybookConfigOverrideInput {
|
|
43
|
+
title?: string | null;
|
|
44
|
+
description?: string | null;
|
|
45
|
+
planes?: readonly PlaybookPlane[] | null;
|
|
46
|
+
onStepFailure?: PlaybookFailurePolicy | null;
|
|
47
|
+
enabled?: boolean | null;
|
|
48
|
+
metadata?: PlaybookMetadata | null;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* What an agent does when a step fails partway through a plan. Playbooks are
|
|
54
|
+
* scripts, never atomic units, so there is no compensation: the definition
|
|
55
|
+
* says only whether the remainder of the plan is abandoned.
|
|
56
|
+
*/
|
|
57
|
+
declare type PlaybookFailurePolicy = 'abort' | 'continue';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Record returned by an intent registry for a declared view intent.
|
|
61
|
+
*
|
|
62
|
+
* Seam for #2588: until an intent registry exists, no resolver is supplied and
|
|
63
|
+
* an intent step fails resolution closed.
|
|
64
|
+
*/
|
|
65
|
+
declare interface PlaybookIntentRecord {
|
|
66
|
+
id: string;
|
|
67
|
+
classification?: CapabilityDeclaration | null;
|
|
68
|
+
planes?: readonly PlaybookPlane[] | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Seam for #2588. */
|
|
72
|
+
declare type PlaybookIntentResolver = (id: string) => PlaybookIntentRecord | null | undefined;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A step naming a declared view intent by its identity (#2588). Valid only
|
|
76
|
+
* where a surface is mounted.
|
|
77
|
+
*/
|
|
78
|
+
declare interface PlaybookIntentStep {
|
|
79
|
+
kind: 'intent';
|
|
80
|
+
/** Declared intent identity from the #2588 intent registry. */
|
|
81
|
+
id: string;
|
|
82
|
+
label?: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
optional?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare type PlaybookMetadata = Record<string, unknown>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolves the capability classification of a referenced model operation.
|
|
91
|
+
*
|
|
92
|
+
* Playbooks never classify a step themselves. A host that knows the emitted
|
|
93
|
+
* build-time classification (core's `tool-schema.ts` output, or the runtime
|
|
94
|
+
* manifest) supplies it here; anything it does not know resolves fail-closed
|
|
95
|
+
* to `{ effect: 'destructive', idempotent: false, openWorld: true }`.
|
|
96
|
+
*/
|
|
97
|
+
declare type PlaybookOperationClassifier = (step: PlaybookOperationStep) => CapabilityDeclaration | null | undefined;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A step naming a model operation by qualified pair — the same qualified
|
|
101
|
+
* form STI discriminators and `@crossPackageRef` already use. Never a
|
|
102
|
+
* generated tool name: that is derived from model, action, and namespace and
|
|
103
|
+
* would silently orphan stored tenant overrides on a namespace change.
|
|
104
|
+
*
|
|
105
|
+
* The step never classifies itself; classification is inherited from the
|
|
106
|
+
* referenced operation (see {@link PlaybookOperationClassifier}).
|
|
107
|
+
*/
|
|
108
|
+
declare interface PlaybookOperationStep {
|
|
109
|
+
kind: 'operation';
|
|
110
|
+
/** Qualified model name, e.g. `@happyvertical/smrt-commerce:Order`. */
|
|
111
|
+
model: string;
|
|
112
|
+
/** Operation name exposed by the model's `@smrt({ api })` surface. */
|
|
113
|
+
action: string;
|
|
114
|
+
/** Human-facing label for the agent's narration of this step. */
|
|
115
|
+
label?: string;
|
|
116
|
+
/** Longer human-facing description of the step. */
|
|
117
|
+
description?: string;
|
|
118
|
+
/** When true, an agent may skip this step without abandoning the plan. */
|
|
119
|
+
optional?: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Execution planes a playbook can declare validity for.
|
|
124
|
+
*
|
|
125
|
+
* `browser` covers WebMCP / in-page agents driving mounted surfaces;
|
|
126
|
+
* `server` covers Node MCP, CLI, and in-app agents running under
|
|
127
|
+
* `executeAsPrincipal`.
|
|
128
|
+
*/
|
|
129
|
+
declare type PlaybookPlane = 'browser' | 'server';
|
|
130
|
+
|
|
131
|
+
/** One step of a resolved plan, with its inherited classification. */
|
|
132
|
+
declare interface PlaybookPlanStep {
|
|
133
|
+
index: number;
|
|
134
|
+
step: PlaybookStep;
|
|
135
|
+
classification: CapabilityClassification;
|
|
136
|
+
/** True when the classification is the fail-closed default. */
|
|
137
|
+
classificationDeclared: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export declare interface PlaybookPreflightAvailableReport {
|
|
141
|
+
available: true;
|
|
142
|
+
/**
|
|
143
|
+
* Literal `true` on every report, as a type-level reminder: a report is a
|
|
144
|
+
* prediction, never a grant, and every step is authorized again where it
|
|
145
|
+
* executes.
|
|
146
|
+
*/
|
|
147
|
+
advisory: true;
|
|
148
|
+
key: string;
|
|
149
|
+
plane: PlaybookPlane;
|
|
150
|
+
title: string;
|
|
151
|
+
description: string;
|
|
152
|
+
verdict: PreflightVerdict;
|
|
153
|
+
steps: readonly PreflightStepReport[];
|
|
154
|
+
summary: PreflightSummary;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** A preflight result. Advisory only — see the two variants above. */
|
|
158
|
+
export declare type PlaybookPreflightReport = PlaybookPreflightAvailableReport | PlaybookPreflightUnavailableReport;
|
|
159
|
+
|
|
160
|
+
export declare interface PlaybookPreflightRequest {
|
|
161
|
+
key: string;
|
|
162
|
+
plane: PlaybookPlane;
|
|
163
|
+
/**
|
|
164
|
+
* Opaque, caller-scoped principal identity used to partition the cache. It is
|
|
165
|
+
* never echoed in the report and never consulted for authority. The cache key
|
|
166
|
+
* also folds in `resolve.tenantId`, so a principal string need not encode the
|
|
167
|
+
* tenant to stay correct.
|
|
168
|
+
*/
|
|
169
|
+
principal: string;
|
|
170
|
+
/** Options handed to `resolvePlaybook()` — the caller's own layer chain. */
|
|
171
|
+
resolve?: ResolvePlaybookOptions;
|
|
172
|
+
/** Evaluates each resolved step. */
|
|
173
|
+
evaluate: PreflightStepEvaluator;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The single, uniform answer for every playbook the caller's own layer chain
|
|
178
|
+
* cannot resolve. An unknown key and an unauthorized key produce this exact
|
|
179
|
+
* frozen value, so preflight is not an enumeration oracle: it carries no key,
|
|
180
|
+
* no plane, and no reason to tell the two apart.
|
|
181
|
+
*/
|
|
182
|
+
export declare interface PlaybookPreflightUnavailableReport {
|
|
183
|
+
available: false;
|
|
184
|
+
advisory: true;
|
|
185
|
+
verdict: 'deny';
|
|
186
|
+
steps: readonly [];
|
|
187
|
+
summary: PreflightSummary;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** A playbook step. Exactly two kinds exist in v1; playbooks cannot nest. */
|
|
191
|
+
declare type PlaybookStep = PlaybookOperationStep | PlaybookIntentStep;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The authority layers preflight reports on. The two planes are deliberately
|
|
195
|
+
* asymmetric and report different layers:
|
|
196
|
+
*
|
|
197
|
+
* - server plane — `tool-allowlist`, `operation-permission`, and `plane` for an
|
|
198
|
+
* intent step;
|
|
199
|
+
* - browser plane — `action-exposure`, `public-access`, `field-permissions`,
|
|
200
|
+
* `app-auth`, and `intent-mount` for an intent step.
|
|
201
|
+
*/
|
|
202
|
+
export declare type PreflightLayer = 'tool-allowlist' | 'operation-permission' | 'plane' | 'action-exposure' | 'public-access' | 'field-permissions' | 'app-auth' | 'intent-mount';
|
|
203
|
+
|
|
204
|
+
/** One authority layer's verdict for one step. */
|
|
205
|
+
export declare interface PreflightLayerReport {
|
|
206
|
+
layer: PreflightLayer;
|
|
207
|
+
verdict: PreflightVerdict;
|
|
208
|
+
reason: PreflightReason;
|
|
209
|
+
/**
|
|
210
|
+
* Permission slugs this layer found missing, when it knows them. Present only
|
|
211
|
+
* on layers that evaluate a slug set.
|
|
212
|
+
*/
|
|
213
|
+
missingPermissions?: readonly string[];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Why a layer reached its verdict. A stable code, never a free-form message. */
|
|
217
|
+
export declare type PreflightReason = 'ok' | 'tool-not-allowed' | 'permission-denied' | 'permission-unknown' | 'plane' | 'action-not-exposed' | 'not-public' | 'auth-required' | 'app-auth-not-evaluated' | 'app-auth-not-configured' | 'fields-redacted' | 'field-permissions-unknown' | 'intent-not-mounted' | 'intent-bridge-not-evaluated' | 'not-evaluated';
|
|
218
|
+
|
|
219
|
+
/** What a step evaluator returns; `preflightPlan` supplies `index` and `kind`. */
|
|
220
|
+
export declare interface PreflightStepEvaluation {
|
|
221
|
+
layers: readonly PreflightLayerReport[];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Evaluates one resolved plan step without executing any part of it. */
|
|
225
|
+
export declare type PreflightStepEvaluator = (step: PlaybookPlanStep) => PreflightStepEvaluation | Promise<PreflightStepEvaluation>;
|
|
226
|
+
|
|
227
|
+
/** A step's aggregate verdict plus the per-layer detail behind it. */
|
|
228
|
+
export declare interface PreflightStepReport {
|
|
229
|
+
index: number;
|
|
230
|
+
kind: PlaybookStep['kind'];
|
|
231
|
+
verdict: PreflightVerdict;
|
|
232
|
+
/** Reason of the first layer that produced the step's aggregate verdict. */
|
|
233
|
+
reason: PreflightReason;
|
|
234
|
+
layers: readonly PreflightLayerReport[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export declare interface PreflightSummary {
|
|
238
|
+
allow: number;
|
|
239
|
+
deny: number;
|
|
240
|
+
unknown: number;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Verdict for one authority layer, one step, or a whole plan.
|
|
245
|
+
*
|
|
246
|
+
* `unknown` is a first-class answer, never a rounded-down `allow`: a layer that
|
|
247
|
+
* cannot be evaluated without executing something says so.
|
|
248
|
+
*/
|
|
249
|
+
export declare type PreflightVerdict = 'allow' | 'deny' | 'unknown';
|
|
250
|
+
|
|
251
|
+
declare interface ResolvePlaybookOptions {
|
|
252
|
+
db?: SmrtClassOptions['db'];
|
|
253
|
+
tenantId?: string | null;
|
|
254
|
+
/** Plane the calling agent runs on. Defaults to `'server'`. */
|
|
255
|
+
plane?: PlaybookPlane;
|
|
256
|
+
/** Highest-precedence layer, supplied per call. */
|
|
257
|
+
override?: PlaybookConfigOverrideInput;
|
|
258
|
+
/** Host-supplied classification source for model-operation steps. */
|
|
259
|
+
classifier?: PlaybookOperationClassifier;
|
|
260
|
+
/** Host-supplied intent registry (#2588). */
|
|
261
|
+
intents?: PlaybookIntentResolver;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export declare interface ServerPreflightOptions {
|
|
265
|
+
/** `PrincipalRun.isToolAllowed` for the slug that gates this step. */
|
|
266
|
+
isToolAllowed(step: PlaybookOperationStep): boolean;
|
|
267
|
+
/**
|
|
268
|
+
* The operation-permission predicate. Evaluates the catalog gate for the
|
|
269
|
+
* step's `(collection, action)` without performing the operation.
|
|
270
|
+
*/
|
|
271
|
+
checkOperationPermission(step: PlaybookOperationStep): PreflightVerdict | Promise<PreflightVerdict>;
|
|
272
|
+
/** Declared plane validity of a view intent, from the #2588 registry. */
|
|
273
|
+
intentPlanes?(id: string): readonly PlaybookPlane[] | null | undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export { }
|
|
File without changes
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-playbooks",
|
|
6
|
-
"packageVersion": "0.
|
|
6
|
+
"packageVersion": "0.45.0",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "6a73c60bfa6bdd46086921228a22036a9d970c037b2d5d62dc7f74a2dd9def9a",
|
|
11
|
+
"packageJson": "85bbd2dbec933ca00b3f455882ca52b5005513727f3f460fe385e0f70f35ae6b",
|
|
12
|
+
"agents": "db7f96be7f32be81a0df3e55532da96bb0bace194fb4314feafacbdeafebf6ae"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -86,7 +86,68 @@
|
|
|
86
86
|
"returns": "Promise<PlaybookOverride | null>"
|
|
87
87
|
}
|
|
88
88
|
],
|
|
89
|
-
"surfaces": [
|
|
89
|
+
"surfaces": [
|
|
90
|
+
{
|
|
91
|
+
"kind": "api",
|
|
92
|
+
"name": "playbookoverrides.getAppOverride",
|
|
93
|
+
"operation": "getAppOverride",
|
|
94
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
95
|
+
"path": "/playbookoverrides/getAppOverride",
|
|
96
|
+
"method": "POST"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"kind": "api",
|
|
100
|
+
"name": "playbookoverrides.getTenantOverride",
|
|
101
|
+
"operation": "getTenantOverride",
|
|
102
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
103
|
+
"path": "/playbookoverrides/getTenantOverride",
|
|
104
|
+
"method": "POST"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"kind": "api",
|
|
108
|
+
"name": "playbookoverrides.getResolutionLayers",
|
|
109
|
+
"operation": "getResolutionLayers",
|
|
110
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
111
|
+
"path": "/playbookoverrides/getResolutionLayers",
|
|
112
|
+
"method": "POST"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"kind": "cli",
|
|
116
|
+
"name": "playbookoverridecollection_getAppOverride",
|
|
117
|
+
"operation": "getAppOverride",
|
|
118
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"kind": "cli",
|
|
122
|
+
"name": "playbookoverridecollection_getTenantOverride",
|
|
123
|
+
"operation": "getTenantOverride",
|
|
124
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"kind": "cli",
|
|
128
|
+
"name": "playbookoverridecollection_getResolutionLayers",
|
|
129
|
+
"operation": "getResolutionLayers",
|
|
130
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"kind": "mcp",
|
|
134
|
+
"name": "playbookoverridecollection_getappoverride",
|
|
135
|
+
"operation": "getAppOverride",
|
|
136
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"kind": "mcp",
|
|
140
|
+
"name": "playbookoverridecollection_gettenantoverride",
|
|
141
|
+
"operation": "getTenantOverride",
|
|
142
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"kind": "mcp",
|
|
146
|
+
"name": "playbookoverridecollection_getresolutionlayers",
|
|
147
|
+
"operation": "getResolutionLayers",
|
|
148
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
149
|
+
}
|
|
150
|
+
],
|
|
90
151
|
"relationshipFeatures": [
|
|
91
152
|
"uuidColumns"
|
|
92
153
|
],
|
|
@@ -283,6 +344,66 @@
|
|
|
283
344
|
}
|
|
284
345
|
],
|
|
285
346
|
"surfaces": [
|
|
347
|
+
{
|
|
348
|
+
"kind": "api",
|
|
349
|
+
"name": "playbookoverrides.getAppOverride",
|
|
350
|
+
"operation": "getAppOverride",
|
|
351
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
352
|
+
"path": "/playbookoverrides/getAppOverride",
|
|
353
|
+
"method": "POST"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"kind": "api",
|
|
357
|
+
"name": "playbookoverrides.getTenantOverride",
|
|
358
|
+
"operation": "getTenantOverride",
|
|
359
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
360
|
+
"path": "/playbookoverrides/getTenantOverride",
|
|
361
|
+
"method": "POST"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"kind": "api",
|
|
365
|
+
"name": "playbookoverrides.getResolutionLayers",
|
|
366
|
+
"operation": "getResolutionLayers",
|
|
367
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection",
|
|
368
|
+
"path": "/playbookoverrides/getResolutionLayers",
|
|
369
|
+
"method": "POST"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"kind": "cli",
|
|
373
|
+
"name": "playbookoverridecollection_getAppOverride",
|
|
374
|
+
"operation": "getAppOverride",
|
|
375
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"kind": "cli",
|
|
379
|
+
"name": "playbookoverridecollection_getTenantOverride",
|
|
380
|
+
"operation": "getTenantOverride",
|
|
381
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"kind": "cli",
|
|
385
|
+
"name": "playbookoverridecollection_getResolutionLayers",
|
|
386
|
+
"operation": "getResolutionLayers",
|
|
387
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
"kind": "mcp",
|
|
391
|
+
"name": "playbookoverridecollection_getappoverride",
|
|
392
|
+
"operation": "getAppOverride",
|
|
393
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"kind": "mcp",
|
|
397
|
+
"name": "playbookoverridecollection_gettenantoverride",
|
|
398
|
+
"operation": "getTenantOverride",
|
|
399
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"kind": "mcp",
|
|
403
|
+
"name": "playbookoverridecollection_getresolutionlayers",
|
|
404
|
+
"operation": "getResolutionLayers",
|
|
405
|
+
"objectName": "@happyvertical/smrt-playbooks:PlaybookOverrideCollection"
|
|
406
|
+
},
|
|
286
407
|
{
|
|
287
408
|
"kind": "api",
|
|
288
409
|
"name": "playbookoverrides.list",
|
|
@@ -363,5 +484,5 @@
|
|
|
363
484
|
"polymorphicAssociations": 0,
|
|
364
485
|
"uuidColumns": 2
|
|
365
486
|
},
|
|
366
|
-
"agentDoc": "# smrt-playbooks\n\nLayered playbook registry, app/tenant overrides, and plan resolution. A\nplaybook is a named, described, layered sequence of steps an agent follows;\nbrowser agents, in-app agents, the Node MCP server, and the CLI all follow the\nsame resolved plan. Fourth instance of the `smrt-prompts` layered-override\npattern — keep it consistent with `prompts`, `languages`, and `features`.\n\n## Core pieces\n\n- `definePlaybook()` registers code defaults in a global process registry\n (`globalThis.__smrtPlaybookRegistry`), so a package-bundled playbook resolves\n with no application registration\n- `resolvePlaybook()` merges the layers and returns a `PlaybookResolution`\n- `PlaybookOverride` (`_smrt_playbook_overrides`) stores partial app-level and\n tenant-level overrides with write-time validation\n- `PlaybookOverrideCollection` exposes the standard SmrtCollection CRUD surface\n\n## Resolution layers (priority low → high)\n\n1. Code default — `definePlaybook({ key, title, description, steps })`\n2. File/config override — `getPackageConfig<PlaybookPackageConfig>('playbooks', defaults)`\n3. App-level stored override — `PlaybookOverride` row with `tenantId = null`\n4. Tenant-level stored override — `PlaybookOverride` row with the current tenant\n5. Runtime override — passed to `resolvePlaybook(key, { override })`\n\nInheritance is field-by-field: a stored column is nullable, and `null` means\n\"use the lower layer\".\n\n## Script semantics\n\nA playbook resolves to a plan the agent executes step by step. **It never\nexecutes as a unit** (epic #2585 invariant 4), so it is never an authority\nboundary and adds no new security object to review. Each step is authorized\nindependently — at the REST boundary in the browser, or by\n`PrincipalRun.assertToolAllowed()` server-side. Nothing in this package\nexecutes a step, and there is deliberately no executor export.\n\nPlaybooks are consequently never atomic and have no compensation.\n`onStepFailure` (`'abort'` | `'continue'`) is the whole of the contract for\nwhat an agent does when step 3 of 5 fails.\n\n## Steps\n\nExactly two kinds in v1, and playbooks cannot nest:\n\n- `{ kind: 'operation', model: '@happyvertical/smrt-commerce:Order', action: 'submit' }`\n — the qualified pair already used by STI discriminators and\n `@crossPackageRef`. **Never a generated tool name**: that is derived from\n model, action, and namespace, and would silently orphan stored tenant\n overrides on a namespace change.\n- `{ kind: 'intent', id }` — a view intent named by its declared #2588\n identity. Valid only where a surface is mounted.\n\nA step referencing another playbook is rejected in `normalizeSteps()` at\ndefinition time.\n\n### Classification is inherited, never self-declared\n\nA step never classifies itself. `resolvePlaybook()` takes a\n`classifier` (and, for intents, an `intents` registry) supplied by the host,\nwhich returns the `CapabilityDeclaration` emitted for the referenced operation\nby `@happyvertical/smrt-types`. Anything undeclared resolves fail-closed to\n`{ effect: 'destructive', idempotent: false, openWorld: true }`. The\nvocabulary itself is older and lower and shared with model tools — it lives in\n`smrt-types`, not here (owning it here would close a\ncore → playbooks → core cycle).\n\n## Plane validity\n\nA playbook declares `planes: readonly ('browser' | 'server')[]`. Resolution for\na caller on an undeclared plane fails closed with reason\n`'plane-not-declared'`.\n\n- Operation-only playbooks default to both planes.\n- A playbook containing a view-intent step defaults to `['browser']`. Server\n validity rides the shipped #2446 browser command/ack bridge, which lets a\n server-side agent drive mounted surfaces with acknowledgement — it must be\n **declared explicitly**, never assumed.\n- The same default applies one level down: a `PlaybookIntentRecord` that\n declares no `planes` is browser-only, so server validity must be declared at\n **both** the playbook and the intent. Silence from the intent registry never\n widens a plane (`'intent-plane-not-declared'`).\n\nThe `#2588` intent registry does not exist yet, so an intent step with no\n`intents` resolver supplied fails closed with\n`'intent-registry-unavailable'`. That resolver is the seam #2588 wires into.\n\n## Editability\n\n`editable` defaults **all-false**, matching `normalizeEditableConfig` in\n`smrt-prompts`. Every stored column has a flag — `title`, `description`,\n`planes`, `onStepFailure`, `enabled`, `metadata` — and `save()` rejects a\nnon-null value for any field the definition has not opted in. `onStepFailure`\nis gated like the rest: flipping a locked playbook from `'abort'` to\n`'continue'` would change what an agent does after a failed prerequisite.\n\n`steps` is **structurally** non-editable, not merely defaulted false:\n\n- `PlaybookEditableConfig` has no `steps` key, and marking one throws at\n definition time\n- `_smrt_playbook_overrides` has **no `steps` column**, so no write of any kind\n has anywhere to put a step list\n- `PlaybookOverride.save()` rejects a `steps` property assigned through the\n untyped option bag rather than dropping it silently\n- `normalizePlaybookLayer()` throws on a `steps` key from the config or runtime\n layer\n- the resolver reads steps only from `PlaybookRegistry`\n\nThe reason is not escalation — under the script model a tenant cannot escalate,\nsince every step is authorized independently regardless of who wrote the list.\nIt is that an agent announcing \"checking out your cart\" while an overridden\nstep list does something else is a description-behavior mismatch.\n\nEnablement overrides are one-directional: a layer may disable, never re-enable\nwhat a lower layer disabled. Enforced in `mergePlaybookLayers()` (`enabled &&\nlayer.enabled`) and rejected at `save()` with a specific message. Plane lists\nnarrow the same way.\n\n## Caching\n\nResolutions are cached per `(key, tenantId, db)` with a TTL. The cache is\ninvalidated on `PlaybookOverride.save()` and `.delete()`; an app-level write\n(`tenantId = null`) clears every tenant's entry for that key, because each\ntenant inherits from it. Use `clearPlaybookCache()` in tests.\n\nA monotonic per-`(db, key)` invalidation generation closes the read-racing-a-\nwrite window; see the Gotchas entry below before touching `cache.ts`.\n\n## Gotchas\n\n- **`context` carries the tenant scope.** `save()` sets\n `this.context = this.tenantId ?? '__app__'` and `conflictColumns` is\n `['key', 'context']`. `tenantId` is nullable, and a unique index over it\n would let multiple NULL rows coexist on PostgreSQL and DuckDB. The `context`\n trick (from `smrt-languages`) is what makes the same upsert correct on all\n three dialects — do not \"simplify\" it to `['key', 'tenantId']`.\n- **Identity changes need the delete-then-insert dance.** Changing `key` or\n `tenantId` on an existing row changes the conflict identity, so a plain\n `super.save()` writes the old primary key under a new one. Same handling as\n `PromptOverride` / `LanguageOverride`: a transaction where the driver has\n one, otherwise a staged replacement row deleted only after the new row is\n durable.\n- **JSON fields are stored as strings.** `planes` and `metadata` are text\n columns with guarded `getPlanes()` / `setPlanes()` / `getMetadata()` /\n `setMetadata()` helpers that swallow parse errors. Never override\n `toJSON()`; extend serialization through `transformJSON()`.\n- **`resolvePlaybook()` returns a result, it does not throw for policy.**\n Unknown key, disabled, wrong plane, and unresolvable intents all come back as\n `{ ok: false, reason, message }`. It *does* throw for programming errors —\n notably a `steps` key on an override layer.\n- **The cache carries an invalidation generation, and this is where it\n diverges from `smrt-prompts`.** A resolution captures\n `getPlaybookCacheGeneration(key, db)` before its asynchronous layer loads and\n hands it back to `setCachedPlaybookBase()`; a concurrent `save()` / `delete()`\n bumps the generation, and the in-flight resolution is then refused the cache\n write instead of repopulating the key it just invalidated with the pre-write\n value. Without it, \"a stale entry is never served after a write\" held only\n until a read raced a write, and then failed for the full 30s TTL. Generations\n are tracked per `(db, key)`, not per tenant, because an app-level row is\n inherited by every tenant. `clearPlaybookCache()` bumps rather than resets\n them, so a resolution that started before the clear cannot write back either.\n `smrt-prompts` and `smrt-languages` still carry the unguarded version of this\n race; fixing them is separate work.\n- **Restart vitest after adding a decorated class**; the manifest is generated\n at startup.\n\n## Related\n\n- `@happyvertical/smrt-prompts` — the pattern this package clones\n- `@happyvertical/smrt-languages` — source of the `context` column convention\n- `@happyvertical/smrt-features` — parallel package for feature flags\n- Epic #2585 — declared agent surface; #2587 capability vocabulary,\n #2588 view intents, #2590 preflight, #2591 manifest emission\n"
|
|
487
|
+
"agentDoc": "# smrt-playbooks\n\nLayered playbook registry, app/tenant overrides, and plan resolution. A\nplaybook is a named, described, layered sequence of steps an agent follows;\nbrowser agents, in-app agents, the Node MCP server, and the CLI all follow the\nsame resolved plan. Fourth instance of the `smrt-prompts` layered-override\npattern — keep it consistent with `prompts`, `languages`, and `features`.\n\n## Core pieces\n\n- `definePlaybook()` registers code defaults in a global process registry\n (`globalThis.__smrtPlaybookRegistry`), so a package-bundled playbook resolves\n with no application registration\n- `resolvePlaybook()` merges the layers and returns a `PlaybookResolution`\n- `PlaybookOverride` (`_smrt_playbook_overrides`) stores partial app-level and\n tenant-level overrides with write-time validation\n- `PlaybookOverrideCollection` exposes the standard SmrtCollection CRUD surface\n\n## Resolution layers (priority low → high)\n\n1. Code default — `definePlaybook({ key, title, description, steps })`\n2. File/config override — `getPackageConfig<PlaybookPackageConfig>('playbooks', defaults)`\n3. App-level stored override — `PlaybookOverride` row with `tenantId = null`\n4. Tenant-level stored override — `PlaybookOverride` row with the current tenant\n5. Runtime override — passed to `resolvePlaybook(key, { override })`\n\nInheritance is field-by-field: a stored column is nullable, and `null` means\n\"use the lower layer\".\n\n## Script semantics\n\nA playbook resolves to a plan the agent executes step by step. **It never\nexecutes as a unit** (epic #2585 invariant 4), so it is never an authority\nboundary and adds no new security object to review. Each step is authorized\nindependently — at the REST boundary in the browser, or by\n`PrincipalRun.assertToolAllowed()` server-side. Nothing in this package\nexecutes a step, and there is deliberately no executor export.\n\nPlaybooks are consequently never atomic and have no compensation.\n`onStepFailure` (`'abort'` | `'continue'`) is the whole of the contract for\nwhat an agent does when step 3 of 5 fails.\n\n## Steps\n\nExactly two kinds in v1, and playbooks cannot nest:\n\n- `{ kind: 'operation', model: '@happyvertical/smrt-commerce:Order', action: 'submit' }`\n — the qualified pair already used by STI discriminators and\n `@crossPackageRef`. **Never a generated tool name**: that is derived from\n model, action, and namespace, and would silently orphan stored tenant\n overrides on a namespace change.\n- `{ kind: 'intent', id }` — a view intent named by its declared #2588\n identity. Valid only where a surface is mounted.\n\nA step referencing another playbook is rejected in `normalizeSteps()` at\ndefinition time.\n\n### Classification is inherited, never self-declared\n\nA step never classifies itself. `resolvePlaybook()` takes a\n`classifier` (and, for intents, an `intents` registry) supplied by the host,\nwhich returns the `CapabilityDeclaration` emitted for the referenced operation\nby `@happyvertical/smrt-types`. Anything undeclared resolves fail-closed to\n`{ effect: 'destructive', idempotent: false, openWorld: true }`. The\nvocabulary itself is older and lower and shared with model tools — it lives in\n`smrt-types`, not here (owning it here would close a\ncore → playbooks → core cycle).\n\n## Plane validity\n\nA playbook declares `planes: readonly ('browser' | 'server')[]`. Resolution for\na caller on an undeclared plane fails closed with reason\n`'plane-not-declared'`.\n\n- Operation-only playbooks default to both planes.\n- A playbook containing a view-intent step defaults to `['browser']`. Server\n validity rides the shipped #2446 browser command/ack bridge, which lets a\n server-side agent drive mounted surfaces with acknowledgement — it must be\n **declared explicitly**, never assumed.\n- The same default applies one level down: a `PlaybookIntentRecord` that\n declares no `planes` is browser-only, so server validity must be declared at\n **both** the playbook and the intent. Silence from the intent registry never\n widens a plane (`'intent-plane-not-declared'`).\n\nThe `#2588` intent registry does not exist yet, so an intent step with no\n`intents` resolver supplied fails closed with\n`'intent-registry-unavailable'`. That resolver is the seam #2588 wires into.\n\n## Editability\n\n`editable` defaults **all-false**, matching `normalizeEditableConfig` in\n`smrt-prompts`. Every stored column has a flag — `title`, `description`,\n`planes`, `onStepFailure`, `enabled`, `metadata` — and `save()` rejects a\nnon-null value for any field the definition has not opted in. `onStepFailure`\nis gated like the rest: flipping a locked playbook from `'abort'` to\n`'continue'` would change what an agent does after a failed prerequisite.\n\n`steps` is **structurally** non-editable, not merely defaulted false:\n\n- `PlaybookEditableConfig` has no `steps` key, and marking one throws at\n definition time\n- `_smrt_playbook_overrides` has **no `steps` column**, so no write of any kind\n has anywhere to put a step list\n- `PlaybookOverride.save()` rejects a `steps` property assigned through the\n untyped option bag rather than dropping it silently\n- `normalizePlaybookLayer()` throws on a `steps` key from the config or runtime\n layer\n- the resolver reads steps only from `PlaybookRegistry`\n\nThe reason is not escalation — under the script model a tenant cannot escalate,\nsince every step is authorized independently regardless of who wrote the list.\nIt is that an agent announcing \"checking out your cart\" while an overridden\nstep list does something else is a description-behavior mismatch.\n\nEnablement overrides are one-directional: a layer may disable, never re-enable\nwhat a lower layer disabled. Enforced in `mergePlaybookLayers()` (`enabled &&\nlayer.enabled`) and rejected at `save()` with a specific message. Plane lists\nnarrow the same way.\n\n## Preflight (#2590)\n\n`preflightPlaybook({ key, plane, principal, resolve, evaluate })` resolves a\nplaybook through the **caller's own layer chain**, decomposes it, and returns a\nper-step verdict — `allow` | `deny` | `unknown` — plus an aggregate. It executes\nnothing.\n\n**Advisory only. Preflight predicts; it never grants.** Every step re-enforces at\nexecution, unconditionally. Without that, a permission revoked mid-playbook would\nleave a cached \"allowed\" standing — a time-of-check/time-of-use bypass. That\nconstraint is also what makes the cache free: a stale `allow` costs a\ncorrectly-denied step, a stale `deny` costs a briefly hidden capability that\nexpires on the TTL, and neither is a security event.\n\nThe two planes are **not symmetric**, deliberately:\n\n| | Server (`createServerStepEvaluator`) | Browser (`createBrowserStepEvaluator`) |\n|---|---|---|\n| Layers | `tool-allowlist`, `operation-permission` | `action-exposure`, `public-access`, `field-permissions`, `app-auth` |\n| Source | `PrincipalRun.isToolAllowed` + the operation-permission predicate | `isApiActionEnabled`, `isRoutePublic`, field read-permission slugs |\n| Intent step | `plane` — denied unless the intent declares `server` | `intent-mount` — always `unknown` |\n| App auth | n/a (the predicate *is* the gate) | **`unknown`** — never evaluated |\n\nBrowser preflight covers the **static layers only**. Generated REST auth is\n`authMiddleware?: (objectName, action) => (req) => Promise<Request | Response>`:\nrequest-bound, `Response`-returning rather than boolean, and free to consult\nsession stores, rate-limit, or audit. It is not a dry-run predicate, so preflight\n**never invokes it**, synthetically or otherwise — the `_preflight` route's\noptions in `smrt-core` carry no auth handle at all, only the boolean\n`appAuthConfigured`. An optional `authPredicate` seam can later be added to\n`BrowserPreflightLayerSource` and turn the `app-auth` `unknown` into a real\nverdict **without changing the report contract**.\n\n`createBrowserPlaybookPreflight()` (in `rest-preflight.ts`) is the provider wired\ninto `APIConfig.playbookPreflight`. Core owns the route and the static-layer\nfacts because `ObjectRegistry` is core's; this package owns resolution and the\nverdict vocabulary — so the dependency stays one-way.\n\n### Not an oracle\n\nEvery playbook the caller's chain cannot resolve — unknown key, disabled,\nwrong-plane, unresolvable intent, or an error thrown anywhere in resolution or\nevaluation — returns the single frozen `PLAYBOOK_PREFLIGHT_UNAVAILABLE` value: no\nkey echo, no reason, no message, and served by the route with an unconditional\n200. An unknown key and an unauthorized key are byte-identical.\n\nTiming is held in the same class from both ends. The unknown-key path pays the\nsame override-layer read a resolvable key pays (`equalizeUnknownKeyCost`),\nbecause `resolvePlaybook()` short-circuits a registry miss before touching the\ndatabase; and unavailable results are cached for unknown and\nregistered-but-unavailable keys **alike**, because caching only one of them would\nput every *repeat* probe of the other in a different timing class. Growth from\nprobing random keys is bounded where it belongs — the preflight cache is capped\nwith expiry-first eviction — not by declining to cache.\n\n### Verdict rules worth knowing\n\n- `deny` beats `unknown` beats `allow` (`worstVerdict`), and a step's `reason` is\n always the first layer that produced its verdict — reason and verdict can never\n describe different layers.\n- Evaluation never short-circuits: preflight exists to say *which* step of five\n would die.\n- A missing **field** read-permission slug redacts a field, it does not fail the\n step, so `field-permissions` stays `allow` with `reason: 'fields-redacted'` and\n the missing slugs attached. Reporting `deny` would predict a failure that will\n not happen.\n- A non-public route with **no** middleware wired is a real, statically knowable\n `deny` (the generator's fail-closed 401). With one wired it is `unknown`.\n- A tool listing may filter on preflight (`filterPlaybooksByPreflight` in\n `smrt-agents`), but that filter is a listing convenience and **never**\n load-bearing for authorization.\n\n## Caching\n\nResolutions are cached per `(key, tenantId, db)` with a TTL. The cache is\ninvalidated on `PlaybookOverride.save()` and `.delete()`; an app-level write\n(`tenantId = null`) clears every tenant's entry for that key, because each\ntenant inherits from it. Use `clearPlaybookCache()` in tests.\n\nA monotonic per-`(db, key)` invalidation generation closes the read-racing-a-\nwrite window; see the Gotchas entry below before touching `cache.ts`.\n\nPreflight results cache separately, per `(principal, key, plane, tenant)`, with a\nshorter TTL and **no invalidation ceremony of their own** — an entry captured\nunder an older generation of the playbook cache is dropped on read. `principal`\nis an opaque, caller-scoped partition key: it is never echoed in a report and\nnever consulted for authority. Use `clearPlaybookPreflightCache()` in tests.\n\nTwo partitioning traps, both of which produce a cross-context read rather than a\nstale one:\n\n- **The tenant is resolved, not defaulted.** `preflightPlaybook()` applies the\n same `tenantId !== undefined ? tenantId : (getTenantId() ?? null)` fallback\n `loadPlaybookBase()` does. Scoping an omitted tenant to `null` would let two\n ambient `withTenant()` callers with one principal share an entry — and the\n report carries the resolved title and description.\n- **The principal must cover every input the evaluation reads.** The REST\n provider's default folds in `appAuthConfigured` (it decides whether\n `public-access` / `app-auth` are verdicts or `unknown`) and distinguishes an\n *absent* permission set (`perm:unpublished`, field layer `unknown`) from an\n explicitly empty one (known, `allow` with redactions). A custom `principal`\n must do the same.\n\n## Gotchas\n\n- **`context` carries the tenant scope.** `save()` sets\n `this.context = this.tenantId ?? '__app__'` and `conflictColumns` is\n `['key', 'context']`. `tenantId` is nullable, and a unique index over it\n would let multiple NULL rows coexist on PostgreSQL and DuckDB. The `context`\n trick (from `smrt-languages`) is what makes the same upsert correct on all\n three dialects — do not \"simplify\" it to `['key', 'tenantId']`.\n- **Identity changes need the delete-then-insert dance.** Changing `key` or\n `tenantId` on an existing row changes the conflict identity, so a plain\n `super.save()` writes the old primary key under a new one. Same handling as\n `PromptOverride` / `LanguageOverride`: a transaction where the driver has\n one, otherwise a staged replacement row deleted only after the new row is\n durable.\n- **JSON fields are stored as strings.** `planes` and `metadata` are text\n columns with guarded `getPlanes()` / `setPlanes()` / `getMetadata()` /\n `setMetadata()` helpers that swallow parse errors. Never override\n `toJSON()`; extend serialization through `transformJSON()`.\n- **`resolvePlaybook()` returns a result, it does not throw for policy.**\n Unknown key, disabled, wrong plane, and unresolvable intents all come back as\n `{ ok: false, reason, message }`. It *does* throw for programming errors —\n notably a `steps` key on an override layer.\n- **The cache carries an invalidation generation, and this is where it\n diverges from `smrt-prompts`.** A resolution captures\n `getPlaybookCacheGeneration(key, db)` before its asynchronous layer loads and\n hands it back to `setCachedPlaybookBase()`; a concurrent `save()` / `delete()`\n bumps the generation, and the in-flight resolution is then refused the cache\n write instead of repopulating the key it just invalidated with the pre-write\n value. Without it, \"a stale entry is never served after a write\" held only\n until a read raced a write, and then failed for the full 30s TTL. Generations\n are tracked per `(db, key)`, not per tenant, because an app-level row is\n inherited by every tenant. `clearPlaybookCache()` bumps rather than resets\n them, so a resolution that started before the clear cannot write back either.\n `smrt-prompts` and `smrt-languages` still carry the unguarded version of this\n race; fixing them is separate work.\n- **Restart vitest after adding a decorated class**; the manifest is generated\n at startup.\n\n## Related\n\n- `@happyvertical/smrt-prompts` — the pattern this package clones\n- `@happyvertical/smrt-languages` — source of the `context` column convention\n- `@happyvertical/smrt-features` — parallel package for feature flags\n- Epic #2585 — declared agent surface; #2587 capability vocabulary,\n #2588 view intents, #2590 preflight, #2591 manifest emission\n"
|
|
367
488
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-playbooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Layered playbook registry, app/tenant overrides, and plan resolution for SMRT agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,17 +20,17 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@happyvertical/sql": "^0.89.4",
|
|
23
|
-
"@happyvertical/smrt-
|
|
24
|
-
"@happyvertical/smrt-
|
|
25
|
-
"@happyvertical/smrt-
|
|
26
|
-
"@happyvertical/smrt-
|
|
23
|
+
"@happyvertical/smrt-tenancy": "0.45.0",
|
|
24
|
+
"@happyvertical/smrt-types": "0.45.0",
|
|
25
|
+
"@happyvertical/smrt-core": "0.45.0",
|
|
26
|
+
"@happyvertical/smrt-config": "0.45.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "24.13.2",
|
|
30
30
|
"typescript": "5.9.3",
|
|
31
31
|
"vite": "8.1.4",
|
|
32
32
|
"vitest": "4.1.10",
|
|
33
|
-
"@happyvertical/smrt-vitest": "0.
|
|
33
|
+
"@happyvertical/smrt-vitest": "0.45.0"
|
|
34
34
|
},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"smrt",
|