@happyvertical/smrt-playbooks 0.44.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 +174 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +95 -0
- package/dist/index.d.ts +345 -0
- package/dist/index.js +734 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +424 -0
- package/dist/smrt-knowledge.json +367 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# smrt-playbooks
|
|
2
|
+
|
|
3
|
+
Layered playbook registry, app/tenant overrides, and plan resolution. A
|
|
4
|
+
playbook is a named, described, layered sequence of steps an agent follows;
|
|
5
|
+
browser agents, in-app agents, the Node MCP server, and the CLI all follow the
|
|
6
|
+
same resolved plan. Fourth instance of the `smrt-prompts` layered-override
|
|
7
|
+
pattern — keep it consistent with `prompts`, `languages`, and `features`.
|
|
8
|
+
|
|
9
|
+
## Core pieces
|
|
10
|
+
|
|
11
|
+
- `definePlaybook()` registers code defaults in a global process registry
|
|
12
|
+
(`globalThis.__smrtPlaybookRegistry`), so a package-bundled playbook resolves
|
|
13
|
+
with no application registration
|
|
14
|
+
- `resolvePlaybook()` merges the layers and returns a `PlaybookResolution`
|
|
15
|
+
- `PlaybookOverride` (`_smrt_playbook_overrides`) stores partial app-level and
|
|
16
|
+
tenant-level overrides with write-time validation
|
|
17
|
+
- `PlaybookOverrideCollection` exposes the standard SmrtCollection CRUD surface
|
|
18
|
+
|
|
19
|
+
## Resolution layers (priority low → high)
|
|
20
|
+
|
|
21
|
+
1. Code default — `definePlaybook({ key, title, description, steps })`
|
|
22
|
+
2. File/config override — `getPackageConfig<PlaybookPackageConfig>('playbooks', defaults)`
|
|
23
|
+
3. App-level stored override — `PlaybookOverride` row with `tenantId = null`
|
|
24
|
+
4. Tenant-level stored override — `PlaybookOverride` row with the current tenant
|
|
25
|
+
5. Runtime override — passed to `resolvePlaybook(key, { override })`
|
|
26
|
+
|
|
27
|
+
Inheritance is field-by-field: a stored column is nullable, and `null` means
|
|
28
|
+
"use the lower layer".
|
|
29
|
+
|
|
30
|
+
## Script semantics
|
|
31
|
+
|
|
32
|
+
A playbook resolves to a plan the agent executes step by step. **It never
|
|
33
|
+
executes as a unit** (epic #2585 invariant 4), so it is never an authority
|
|
34
|
+
boundary and adds no new security object to review. Each step is authorized
|
|
35
|
+
independently — at the REST boundary in the browser, or by
|
|
36
|
+
`PrincipalRun.assertToolAllowed()` server-side. Nothing in this package
|
|
37
|
+
executes a step, and there is deliberately no executor export.
|
|
38
|
+
|
|
39
|
+
Playbooks are consequently never atomic and have no compensation.
|
|
40
|
+
`onStepFailure` (`'abort'` | `'continue'`) is the whole of the contract for
|
|
41
|
+
what an agent does when step 3 of 5 fails.
|
|
42
|
+
|
|
43
|
+
## Steps
|
|
44
|
+
|
|
45
|
+
Exactly two kinds in v1, and playbooks cannot nest:
|
|
46
|
+
|
|
47
|
+
- `{ kind: 'operation', model: '@happyvertical/smrt-commerce:Order', action: 'submit' }`
|
|
48
|
+
— the qualified pair already used by STI discriminators and
|
|
49
|
+
`@crossPackageRef`. **Never a generated tool name**: that is derived from
|
|
50
|
+
model, action, and namespace, and would silently orphan stored tenant
|
|
51
|
+
overrides on a namespace change.
|
|
52
|
+
- `{ kind: 'intent', id }` — a view intent named by its declared #2588
|
|
53
|
+
identity. Valid only where a surface is mounted.
|
|
54
|
+
|
|
55
|
+
A step referencing another playbook is rejected in `normalizeSteps()` at
|
|
56
|
+
definition time.
|
|
57
|
+
|
|
58
|
+
### Classification is inherited, never self-declared
|
|
59
|
+
|
|
60
|
+
A step never classifies itself. `resolvePlaybook()` takes a
|
|
61
|
+
`classifier` (and, for intents, an `intents` registry) supplied by the host,
|
|
62
|
+
which returns the `CapabilityDeclaration` emitted for the referenced operation
|
|
63
|
+
by `@happyvertical/smrt-types`. Anything undeclared resolves fail-closed to
|
|
64
|
+
`{ effect: 'destructive', idempotent: false, openWorld: true }`. The
|
|
65
|
+
vocabulary itself is older and lower and shared with model tools — it lives in
|
|
66
|
+
`smrt-types`, not here (owning it here would close a
|
|
67
|
+
core → playbooks → core cycle).
|
|
68
|
+
|
|
69
|
+
## Plane validity
|
|
70
|
+
|
|
71
|
+
A playbook declares `planes: readonly ('browser' | 'server')[]`. Resolution for
|
|
72
|
+
a caller on an undeclared plane fails closed with reason
|
|
73
|
+
`'plane-not-declared'`.
|
|
74
|
+
|
|
75
|
+
- Operation-only playbooks default to both planes.
|
|
76
|
+
- A playbook containing a view-intent step defaults to `['browser']`. Server
|
|
77
|
+
validity rides the shipped #2446 browser command/ack bridge, which lets a
|
|
78
|
+
server-side agent drive mounted surfaces with acknowledgement — it must be
|
|
79
|
+
**declared explicitly**, never assumed.
|
|
80
|
+
- The same default applies one level down: a `PlaybookIntentRecord` that
|
|
81
|
+
declares no `planes` is browser-only, so server validity must be declared at
|
|
82
|
+
**both** the playbook and the intent. Silence from the intent registry never
|
|
83
|
+
widens a plane (`'intent-plane-not-declared'`).
|
|
84
|
+
|
|
85
|
+
The `#2588` intent registry does not exist yet, so an intent step with no
|
|
86
|
+
`intents` resolver supplied fails closed with
|
|
87
|
+
`'intent-registry-unavailable'`. That resolver is the seam #2588 wires into.
|
|
88
|
+
|
|
89
|
+
## Editability
|
|
90
|
+
|
|
91
|
+
`editable` defaults **all-false**, matching `normalizeEditableConfig` in
|
|
92
|
+
`smrt-prompts`. Every stored column has a flag — `title`, `description`,
|
|
93
|
+
`planes`, `onStepFailure`, `enabled`, `metadata` — and `save()` rejects a
|
|
94
|
+
non-null value for any field the definition has not opted in. `onStepFailure`
|
|
95
|
+
is gated like the rest: flipping a locked playbook from `'abort'` to
|
|
96
|
+
`'continue'` would change what an agent does after a failed prerequisite.
|
|
97
|
+
|
|
98
|
+
`steps` is **structurally** non-editable, not merely defaulted false:
|
|
99
|
+
|
|
100
|
+
- `PlaybookEditableConfig` has no `steps` key, and marking one throws at
|
|
101
|
+
definition time
|
|
102
|
+
- `_smrt_playbook_overrides` has **no `steps` column**, so no write of any kind
|
|
103
|
+
has anywhere to put a step list
|
|
104
|
+
- `PlaybookOverride.save()` rejects a `steps` property assigned through the
|
|
105
|
+
untyped option bag rather than dropping it silently
|
|
106
|
+
- `normalizePlaybookLayer()` throws on a `steps` key from the config or runtime
|
|
107
|
+
layer
|
|
108
|
+
- the resolver reads steps only from `PlaybookRegistry`
|
|
109
|
+
|
|
110
|
+
The reason is not escalation — under the script model a tenant cannot escalate,
|
|
111
|
+
since every step is authorized independently regardless of who wrote the list.
|
|
112
|
+
It is that an agent announcing "checking out your cart" while an overridden
|
|
113
|
+
step list does something else is a description-behavior mismatch.
|
|
114
|
+
|
|
115
|
+
Enablement overrides are one-directional: a layer may disable, never re-enable
|
|
116
|
+
what a lower layer disabled. Enforced in `mergePlaybookLayers()` (`enabled &&
|
|
117
|
+
layer.enabled`) and rejected at `save()` with a specific message. Plane lists
|
|
118
|
+
narrow the same way.
|
|
119
|
+
|
|
120
|
+
## Caching
|
|
121
|
+
|
|
122
|
+
Resolutions are cached per `(key, tenantId, db)` with a TTL. The cache is
|
|
123
|
+
invalidated on `PlaybookOverride.save()` and `.delete()`; an app-level write
|
|
124
|
+
(`tenantId = null`) clears every tenant's entry for that key, because each
|
|
125
|
+
tenant inherits from it. Use `clearPlaybookCache()` in tests.
|
|
126
|
+
|
|
127
|
+
A monotonic per-`(db, key)` invalidation generation closes the read-racing-a-
|
|
128
|
+
write window; see the Gotchas entry below before touching `cache.ts`.
|
|
129
|
+
|
|
130
|
+
## Gotchas
|
|
131
|
+
|
|
132
|
+
- **`context` carries the tenant scope.** `save()` sets
|
|
133
|
+
`this.context = this.tenantId ?? '__app__'` and `conflictColumns` is
|
|
134
|
+
`['key', 'context']`. `tenantId` is nullable, and a unique index over it
|
|
135
|
+
would let multiple NULL rows coexist on PostgreSQL and DuckDB. The `context`
|
|
136
|
+
trick (from `smrt-languages`) is what makes the same upsert correct on all
|
|
137
|
+
three dialects — do not "simplify" it to `['key', 'tenantId']`.
|
|
138
|
+
- **Identity changes need the delete-then-insert dance.** Changing `key` or
|
|
139
|
+
`tenantId` on an existing row changes the conflict identity, so a plain
|
|
140
|
+
`super.save()` writes the old primary key under a new one. Same handling as
|
|
141
|
+
`PromptOverride` / `LanguageOverride`: a transaction where the driver has
|
|
142
|
+
one, otherwise a staged replacement row deleted only after the new row is
|
|
143
|
+
durable.
|
|
144
|
+
- **JSON fields are stored as strings.** `planes` and `metadata` are text
|
|
145
|
+
columns with guarded `getPlanes()` / `setPlanes()` / `getMetadata()` /
|
|
146
|
+
`setMetadata()` helpers that swallow parse errors. Never override
|
|
147
|
+
`toJSON()`; extend serialization through `transformJSON()`.
|
|
148
|
+
- **`resolvePlaybook()` returns a result, it does not throw for policy.**
|
|
149
|
+
Unknown key, disabled, wrong plane, and unresolvable intents all come back as
|
|
150
|
+
`{ ok: false, reason, message }`. It *does* throw for programming errors —
|
|
151
|
+
notably a `steps` key on an override layer.
|
|
152
|
+
- **The cache carries an invalidation generation, and this is where it
|
|
153
|
+
diverges from `smrt-prompts`.** A resolution captures
|
|
154
|
+
`getPlaybookCacheGeneration(key, db)` before its asynchronous layer loads and
|
|
155
|
+
hands it back to `setCachedPlaybookBase()`; a concurrent `save()` / `delete()`
|
|
156
|
+
bumps the generation, and the in-flight resolution is then refused the cache
|
|
157
|
+
write instead of repopulating the key it just invalidated with the pre-write
|
|
158
|
+
value. Without it, "a stale entry is never served after a write" held only
|
|
159
|
+
until a read raced a write, and then failed for the full 30s TTL. Generations
|
|
160
|
+
are tracked per `(db, key)`, not per tenant, because an app-level row is
|
|
161
|
+
inherited by every tenant. `clearPlaybookCache()` bumps rather than resets
|
|
162
|
+
them, so a resolution that started before the clear cannot write back either.
|
|
163
|
+
`smrt-prompts` and `smrt-languages` still carry the unguarded version of this
|
|
164
|
+
race; fixing them is separate work.
|
|
165
|
+
- **Restart vitest after adding a decorated class**; the manifest is generated
|
|
166
|
+
at startup.
|
|
167
|
+
|
|
168
|
+
## Related
|
|
169
|
+
|
|
170
|
+
- `@happyvertical/smrt-prompts` — the pattern this package clones
|
|
171
|
+
- `@happyvertical/smrt-languages` — source of the `context` column convention
|
|
172
|
+
- `@happyvertical/smrt-features` — parallel package for feature flags
|
|
173
|
+
- Epic #2585 — declared agent surface; #2587 capability vocabulary,
|
|
174
|
+
#2588 view intents, #2590 preflight, #2591 manifest emission
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright <2025> <Happy Vertical Corporation>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @happyvertical/smrt-playbooks
|
|
2
|
+
|
|
3
|
+
Layered playbook definitions, tenant-aware overrides, and plan resolution for
|
|
4
|
+
s-m-r-t agents.
|
|
5
|
+
|
|
6
|
+
A playbook is a named, described sequence of steps an agent follows. It gives a
|
|
7
|
+
multi-step intent — "check out this cart" — a home that is neither a custom
|
|
8
|
+
model action nor a view intent, and browser agents, in-app agents, the Node MCP
|
|
9
|
+
server, and the CLI all follow the same resolved plan.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @happyvertical/smrt-playbooks
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { definePlaybook, resolvePlaybook } from '@happyvertical/smrt-playbooks';
|
|
21
|
+
|
|
22
|
+
// 1. Register a playbook's defaults at startup
|
|
23
|
+
definePlaybook({
|
|
24
|
+
key: 'commerce.cart.checkout',
|
|
25
|
+
title: 'Check out this cart',
|
|
26
|
+
description: 'Submits the order and captures payment.',
|
|
27
|
+
steps: [
|
|
28
|
+
{
|
|
29
|
+
kind: 'operation',
|
|
30
|
+
model: '@happyvertical/smrt-commerce:Order',
|
|
31
|
+
action: 'submit',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
kind: 'operation',
|
|
35
|
+
model: '@happyvertical/smrt-commerce:Payment',
|
|
36
|
+
action: 'capture',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
onStepFailure: 'abort',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// 2. Resolve a plan for a caller on a plane
|
|
43
|
+
const resolution = await resolvePlaybook('commerce.cart.checkout', {
|
|
44
|
+
db,
|
|
45
|
+
plane: 'server',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!resolution.ok) {
|
|
49
|
+
// Fails closed with a specific reason: unknown-playbook, disabled,
|
|
50
|
+
// plane-not-declared, intent-registry-unavailable, unknown-intent.
|
|
51
|
+
throw new Error(resolution.message);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const step of resolution.plan.steps) {
|
|
55
|
+
// The agent executes each step itself; this package never does.
|
|
56
|
+
// step.classification is inherited from the referenced operation.
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## What this package provides
|
|
61
|
+
|
|
62
|
+
- **`definePlaybook()`** — code-first playbook registration in a global process
|
|
63
|
+
registry, so a package ships its own playbooks
|
|
64
|
+
- **`resolvePlaybook()`** — layered resolution: code default → config override →
|
|
65
|
+
stored app override → stored tenant override → runtime override
|
|
66
|
+
- **`PlaybookOverride`** — CRUD model for app-level and tenant-level playbook
|
|
67
|
+
settings, stored in `_smrt_playbook_overrides`
|
|
68
|
+
- **Plane validity** — a playbook declares `browser`, `server`, or both, and
|
|
69
|
+
resolution on an undeclared plane fails closed
|
|
70
|
+
- **TTL cache** keyed by `(key, tenantId)`, invalidated on override save/delete
|
|
71
|
+
|
|
72
|
+
## Guarantees
|
|
73
|
+
|
|
74
|
+
- **A playbook is a script, never a call.** Resolution returns a plan; nothing
|
|
75
|
+
here executes a step. A playbook is therefore never an authority boundary —
|
|
76
|
+
each step is authorized independently where it runs.
|
|
77
|
+
- **Step lists are never editable.** No override layer, including a direct
|
|
78
|
+
model write, can change a playbook's steps. An agent's description of what it
|
|
79
|
+
is about to do always matches the steps it will follow.
|
|
80
|
+
- **Enablement narrows only.** A tenant may disable a playbook; it can never
|
|
81
|
+
enable one a lower layer disabled.
|
|
82
|
+
- **Undeclared classification fails closed** to
|
|
83
|
+
`{ effect: 'destructive', idempotent: false, openWorld: true }`. Steps never
|
|
84
|
+
classify themselves; classification is inherited from the referenced
|
|
85
|
+
operation.
|
|
86
|
+
|
|
87
|
+
## Documentation
|
|
88
|
+
|
|
89
|
+
- See [`AGENTS.md`](./AGENTS.md) for package-internal patterns
|
|
90
|
+
- See [`docs/content/standards.md`](../../docs/content/standards.md) for
|
|
91
|
+
monorepo conventions
|
|
92
|
+
- See related: [`@happyvertical/smrt-prompts`](../prompts) (the layered-override
|
|
93
|
+
pattern this package mirrors),
|
|
94
|
+
[`@happyvertical/smrt-languages`](../languages),
|
|
95
|
+
[`@happyvertical/smrt-features`](../features)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { CapabilityClassification } from '@happyvertical/smrt-types';
|
|
2
|
+
import { CapabilityDeclaration } from '@happyvertical/smrt-types';
|
|
3
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
4
|
+
import { SmrtCollection } from '@happyvertical/smrt-core';
|
|
5
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
6
|
+
import { SmrtObjectOptions } from '@happyvertical/smrt-core';
|
|
7
|
+
|
|
8
|
+
export declare function clearPlaybookCache(): void;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registers a code-default playbook. Packages call this at import time so a
|
|
12
|
+
* bundled playbook resolves without any application registration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function definePlaybook(input: PlaybookDefinitionInput): PlaybookDefinition;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fail-closed capability classification, per epic #2585 invariant 3. Applied
|
|
18
|
+
* whenever the host cannot tell us how a referenced operation is classified.
|
|
19
|
+
*/
|
|
20
|
+
export declare const FAIL_CLOSED_CLASSIFICATION: CapabilityClassification;
|
|
21
|
+
|
|
22
|
+
export declare function getPlaybookCacheTtlMs(): number;
|
|
23
|
+
|
|
24
|
+
export declare function normalizeEditableConfig(editable?: Partial<PlaybookEditableConfig>): PlaybookEditableConfig;
|
|
25
|
+
|
|
26
|
+
/* Excluded from this release type: PACKAGE_VERSION_INITIALIZED */
|
|
27
|
+
|
|
28
|
+
export declare const PLAYBOOK_PLANES: readonly PlaybookPlane[];
|
|
29
|
+
|
|
30
|
+
export declare interface PlaybookAcceptance {
|
|
31
|
+
ok: true;
|
|
32
|
+
plan: PlaybookPlan;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface PlaybookCacheValue {
|
|
36
|
+
key: string;
|
|
37
|
+
title: string;
|
|
38
|
+
description: string;
|
|
39
|
+
planes: readonly PlaybookPlane[];
|
|
40
|
+
onStepFailure: PlaybookFailurePolicy;
|
|
41
|
+
enabled: boolean;
|
|
42
|
+
metadata: PlaybookMetadata;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A partial override from any layer above the code default. There is no
|
|
47
|
+
* `steps` key, and supplying one is rejected rather than ignored.
|
|
48
|
+
*/
|
|
49
|
+
export declare interface PlaybookConfigOverrideInput {
|
|
50
|
+
title?: string | null;
|
|
51
|
+
description?: string | null;
|
|
52
|
+
planes?: readonly PlaybookPlane[] | null;
|
|
53
|
+
onStepFailure?: PlaybookFailurePolicy | null;
|
|
54
|
+
enabled?: boolean | null;
|
|
55
|
+
metadata?: PlaybookMetadata | null;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export declare interface PlaybookDefinition {
|
|
60
|
+
key: string;
|
|
61
|
+
title: string;
|
|
62
|
+
description: string;
|
|
63
|
+
steps: readonly PlaybookStep[];
|
|
64
|
+
planes: readonly PlaybookPlane[];
|
|
65
|
+
onStepFailure: PlaybookFailurePolicy;
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
metadata: PlaybookMetadata;
|
|
68
|
+
editable: PlaybookEditableConfig;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export declare interface PlaybookDefinitionInput {
|
|
72
|
+
key: string;
|
|
73
|
+
title: string;
|
|
74
|
+
description: string;
|
|
75
|
+
steps: readonly PlaybookStep[];
|
|
76
|
+
/**
|
|
77
|
+
* Planes this playbook is valid on. Defaults to both planes when every step
|
|
78
|
+
* is a model operation, and to `['browser']` when any step is a view intent
|
|
79
|
+
* — server validity for an intent-bearing playbook rides the #2446 browser
|
|
80
|
+
* command/ack bridge and must be declared explicitly.
|
|
81
|
+
*/
|
|
82
|
+
planes?: readonly PlaybookPlane[];
|
|
83
|
+
onStepFailure?: PlaybookFailurePolicy;
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
metadata?: PlaybookMetadata | null;
|
|
86
|
+
editable?: Partial<PlaybookEditableConfig>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Fields a stored override layer may change when the definition opts in.
|
|
91
|
+
*
|
|
92
|
+
* `steps` is deliberately absent: it is structurally non-editable, not merely
|
|
93
|
+
* defaulted false. No override layer can carry a step list at all.
|
|
94
|
+
*/
|
|
95
|
+
export declare interface PlaybookEditableConfig {
|
|
96
|
+
title: boolean;
|
|
97
|
+
description: boolean;
|
|
98
|
+
planes: boolean;
|
|
99
|
+
onStepFailure: boolean;
|
|
100
|
+
enabled: boolean;
|
|
101
|
+
metadata: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* What an agent does when a step fails partway through a plan. Playbooks are
|
|
106
|
+
* scripts, never atomic units, so there is no compensation: the definition
|
|
107
|
+
* says only whether the remainder of the plan is abandoned.
|
|
108
|
+
*/
|
|
109
|
+
export declare type PlaybookFailurePolicy = 'abort' | 'continue';
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Record returned by an intent registry for a declared view intent.
|
|
113
|
+
*
|
|
114
|
+
* Seam for #2588: until an intent registry exists, no resolver is supplied and
|
|
115
|
+
* an intent step fails resolution closed.
|
|
116
|
+
*/
|
|
117
|
+
export declare interface PlaybookIntentRecord {
|
|
118
|
+
id: string;
|
|
119
|
+
classification?: CapabilityDeclaration | null;
|
|
120
|
+
planes?: readonly PlaybookPlane[] | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Seam for #2588. */
|
|
124
|
+
export declare type PlaybookIntentResolver = (id: string) => PlaybookIntentRecord | null | undefined;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* A step naming a declared view intent by its identity (#2588). Valid only
|
|
128
|
+
* where a surface is mounted.
|
|
129
|
+
*/
|
|
130
|
+
export declare interface PlaybookIntentStep {
|
|
131
|
+
kind: 'intent';
|
|
132
|
+
/** Declared intent identity from the #2588 intent registry. */
|
|
133
|
+
id: string;
|
|
134
|
+
label?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
optional?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** One normalized override layer. `null` means "clear"; `undefined` means "inherit". */
|
|
140
|
+
export declare interface PlaybookLayer {
|
|
141
|
+
title?: string | null;
|
|
142
|
+
description?: string | null;
|
|
143
|
+
planes?: readonly PlaybookPlane[] | null;
|
|
144
|
+
onStepFailure?: PlaybookFailurePolicy | null;
|
|
145
|
+
enabled?: boolean | null;
|
|
146
|
+
metadata?: PlaybookMetadata | null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export declare type PlaybookMetadata = Record<string, unknown>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Resolves the capability classification of a referenced model operation.
|
|
153
|
+
*
|
|
154
|
+
* Playbooks never classify a step themselves. A host that knows the emitted
|
|
155
|
+
* build-time classification (core's `tool-schema.ts` output, or the runtime
|
|
156
|
+
* manifest) supplies it here; anything it does not know resolves fail-closed
|
|
157
|
+
* to `{ effect: 'destructive', idempotent: false, openWorld: true }`.
|
|
158
|
+
*/
|
|
159
|
+
export declare type PlaybookOperationClassifier = (step: PlaybookOperationStep) => CapabilityDeclaration | null | undefined;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A step naming a model operation by qualified pair — the same qualified
|
|
163
|
+
* form STI discriminators and `@crossPackageRef` already use. Never a
|
|
164
|
+
* generated tool name: that is derived from model, action, and namespace and
|
|
165
|
+
* would silently orphan stored tenant overrides on a namespace change.
|
|
166
|
+
*
|
|
167
|
+
* The step never classifies itself; classification is inherited from the
|
|
168
|
+
* referenced operation (see {@link PlaybookOperationClassifier}).
|
|
169
|
+
*/
|
|
170
|
+
export declare interface PlaybookOperationStep {
|
|
171
|
+
kind: 'operation';
|
|
172
|
+
/** Qualified model name, e.g. `@happyvertical/smrt-commerce:Order`. */
|
|
173
|
+
model: string;
|
|
174
|
+
/** Operation name exposed by the model's `@smrt({ api })` surface. */
|
|
175
|
+
action: string;
|
|
176
|
+
/** Human-facing label for the agent's narration of this step. */
|
|
177
|
+
label?: string;
|
|
178
|
+
/** Longer human-facing description of the step. */
|
|
179
|
+
description?: string;
|
|
180
|
+
/** When true, an agent may skip this step without abandoning the plan. */
|
|
181
|
+
optional?: boolean;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Stored app-level (`tenantId = null`) and tenant-level playbook overrides.
|
|
186
|
+
*
|
|
187
|
+
* There is deliberately **no `steps` column**. `steps` is structurally
|
|
188
|
+
* non-editable: an override layer has nowhere to put a step list, and
|
|
189
|
+
* assigning one is rejected in `save()` rather than silently dropped. That is
|
|
190
|
+
* the description-behavior guarantee from epic #2585 — an agent announcing
|
|
191
|
+
* "checking out your cart" must not be following a rewritten script.
|
|
192
|
+
*/
|
|
193
|
+
export declare class PlaybookOverride extends SmrtObject {
|
|
194
|
+
key: string;
|
|
195
|
+
tenantId: string | null;
|
|
196
|
+
title: string | null;
|
|
197
|
+
description: string | null;
|
|
198
|
+
/** JSON array of plane names; null inherits the lower layer. */
|
|
199
|
+
planes: string | null;
|
|
200
|
+
onStepFailure: string | null;
|
|
201
|
+
/** Tri-state: null inherits, false disables. True can never widen. */
|
|
202
|
+
enabled: boolean | null;
|
|
203
|
+
/** JSON object; stored as a string with guarded get/set helpers. */
|
|
204
|
+
metadata: string | null;
|
|
205
|
+
constructor(options?: PlaybookOverrideOptions);
|
|
206
|
+
getMetadata(): PlaybookMetadata;
|
|
207
|
+
setMetadata(metadata: PlaybookMetadata | null): void;
|
|
208
|
+
getPlanes(): readonly PlaybookPlane[] | null;
|
|
209
|
+
setPlanes(planes: readonly PlaybookPlane[] | null): void;
|
|
210
|
+
toPlaybookLayer(): PlaybookLayer;
|
|
211
|
+
save(): Promise<this>;
|
|
212
|
+
private saveAfterIdentityChange;
|
|
213
|
+
private saveAfterIdentityChangeInTransaction;
|
|
214
|
+
private saveAfterIdentityChangeWithDeferredDelete;
|
|
215
|
+
delete(): Promise<void>;
|
|
216
|
+
private validatePlaybookOverride;
|
|
217
|
+
private getLowerPrecedenceLayers;
|
|
218
|
+
private normalizeForPersistence;
|
|
219
|
+
private getPersistedIdentity;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export declare class PlaybookOverrideCollection extends SmrtCollection<PlaybookOverride> {
|
|
223
|
+
static readonly _itemClass: typeof PlaybookOverride;
|
|
224
|
+
private excludeOverrideId;
|
|
225
|
+
getAppOverride(key: string, options?: {
|
|
226
|
+
excludeId?: string;
|
|
227
|
+
}): Promise<PlaybookOverride | null>;
|
|
228
|
+
getTenantOverride(key: string, tenantId: string, options?: {
|
|
229
|
+
excludeId?: string;
|
|
230
|
+
}): Promise<PlaybookOverride | null>;
|
|
231
|
+
getResolutionLayers(key: string, tenantId?: string | null, options?: {
|
|
232
|
+
excludeId?: string;
|
|
233
|
+
}): Promise<{
|
|
234
|
+
app: PlaybookOverride | null;
|
|
235
|
+
tenant: PlaybookOverride | null;
|
|
236
|
+
}>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export declare interface PlaybookOverrideOptions extends SmrtObjectOptions, PlaybookOverrideOptions_2 {
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
declare interface PlaybookOverrideOptions_2 {
|
|
243
|
+
key?: string;
|
|
244
|
+
tenantId?: string | null;
|
|
245
|
+
title?: string | null;
|
|
246
|
+
description?: string | null;
|
|
247
|
+
planes?: string | readonly PlaybookPlane[] | null;
|
|
248
|
+
onStepFailure?: PlaybookFailurePolicy | null;
|
|
249
|
+
enabled?: boolean | null;
|
|
250
|
+
metadata?: string | PlaybookMetadata | null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export declare interface PlaybookPackageConfig {
|
|
254
|
+
playbooks?: Record<string, PlaybookConfigOverrideInput>;
|
|
255
|
+
[key: string]: unknown;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The output of resolution: a plan an agent follows step by step. Nothing in
|
|
260
|
+
* this package executes a step, and the plan is not an authority boundary —
|
|
261
|
+
* every step is authorized independently where it runs.
|
|
262
|
+
*/
|
|
263
|
+
export declare interface PlaybookPlan {
|
|
264
|
+
key: string;
|
|
265
|
+
title: string;
|
|
266
|
+
description: string;
|
|
267
|
+
plane: PlaybookPlane;
|
|
268
|
+
planes: readonly PlaybookPlane[];
|
|
269
|
+
onStepFailure: PlaybookFailurePolicy;
|
|
270
|
+
metadata: PlaybookMetadata;
|
|
271
|
+
steps: readonly PlaybookPlanStep[];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Execution planes a playbook can declare validity for.
|
|
276
|
+
*
|
|
277
|
+
* `browser` covers WebMCP / in-page agents driving mounted surfaces;
|
|
278
|
+
* `server` covers Node MCP, CLI, and in-app agents running under
|
|
279
|
+
* `executeAsPrincipal`.
|
|
280
|
+
*/
|
|
281
|
+
export declare type PlaybookPlane = 'browser' | 'server';
|
|
282
|
+
|
|
283
|
+
/** One step of a resolved plan, with its inherited classification. */
|
|
284
|
+
export declare interface PlaybookPlanStep {
|
|
285
|
+
index: number;
|
|
286
|
+
step: PlaybookStep;
|
|
287
|
+
classification: CapabilityClassification;
|
|
288
|
+
/** True when the classification is the fail-closed default. */
|
|
289
|
+
classificationDeclared: boolean;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Global process registry of code-default playbooks, keyed by namespaced key.
|
|
294
|
+
*
|
|
295
|
+
* Held on `globalThis` so it survives HMR, mirroring `PromptRegistry`.
|
|
296
|
+
*/
|
|
297
|
+
export declare const PlaybookRegistry: {
|
|
298
|
+
register(input: PlaybookDefinitionInput): PlaybookDefinition;
|
|
299
|
+
get(key: string): PlaybookDefinition | undefined;
|
|
300
|
+
has(key: string): boolean;
|
|
301
|
+
getAll(): PlaybookDefinition[];
|
|
302
|
+
clear(): void;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export declare interface PlaybookRejection {
|
|
306
|
+
ok: false;
|
|
307
|
+
reason: PlaybookRejectionReason;
|
|
308
|
+
message: string;
|
|
309
|
+
key: string;
|
|
310
|
+
/** Index of the offending step, when the rejection is step-scoped. */
|
|
311
|
+
stepIndex?: number;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export declare type PlaybookRejectionReason = 'unknown-playbook' | 'disabled' | 'plane-not-declared' | 'intent-registry-unavailable' | 'unknown-intent' | 'intent-plane-not-declared';
|
|
315
|
+
|
|
316
|
+
/** Resolution fails closed: every rejection carries a specific reason. */
|
|
317
|
+
export declare type PlaybookResolution = PlaybookAcceptance | PlaybookRejection;
|
|
318
|
+
|
|
319
|
+
/** A playbook step. Exactly two kinds exist in v1; playbooks cannot nest. */
|
|
320
|
+
export declare type PlaybookStep = PlaybookOperationStep | PlaybookIntentStep;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Resolves a playbook to a plan for a caller on a given plane.
|
|
324
|
+
*
|
|
325
|
+
* Returns a discriminated result rather than throwing: resolution fails closed
|
|
326
|
+
* and every rejection names a specific reason. Nothing here executes a step —
|
|
327
|
+
* the plan is followed step by step by the agent, and each step is authorized
|
|
328
|
+
* independently at the REST boundary or by `PrincipalRun.assertToolAllowed()`.
|
|
329
|
+
*/
|
|
330
|
+
export declare function resolvePlaybook(key: string, options?: ResolvePlaybookOptions): Promise<PlaybookResolution>;
|
|
331
|
+
|
|
332
|
+
export declare interface ResolvePlaybookOptions {
|
|
333
|
+
db?: SmrtClassOptions['db'];
|
|
334
|
+
tenantId?: string | null;
|
|
335
|
+
/** Plane the calling agent runs on. Defaults to `'server'`. */
|
|
336
|
+
plane?: PlaybookPlane;
|
|
337
|
+
/** Highest-precedence layer, supplied per call. */
|
|
338
|
+
override?: PlaybookConfigOverrideInput;
|
|
339
|
+
/** Host-supplied classification source for model-operation steps. */
|
|
340
|
+
classifier?: PlaybookOperationClassifier;
|
|
341
|
+
/** Host-supplied intent registry (#2588). */
|
|
342
|
+
intents?: PlaybookIntentResolver;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export { }
|