@plumbus/core 0.6.4 → 0.6.5
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/CHANGELOG.md +385 -0
- package/README.md +23 -13
- package/dist/.tsbuildinfo +1 -1
- package/dist/ai/ai/security.d.ts +36 -0
- package/dist/ai/ai-service.d.ts.map +1 -1
- package/dist/ai/ai-service.js +36 -8
- package/dist/ai/ai-service.js.map +1 -1
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +1 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/security.d.ts +9 -0
- package/dist/ai/security.d.ts.map +1 -1
- package/dist/ai/security.js +18 -1
- package/dist/ai/security.js.map +1 -1
- package/dist/ai/types/config.d.ts +11 -0
- package/dist/ai/types/entity.d.ts +15 -0
- package/dist/ai/types/fields.d.ts +51 -0
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/route-generator.d.ts +17 -0
- package/dist/api/route-generator.d.ts.map +1 -1
- package/dist/api/route-generator.js +55 -2
- package/dist/api/route-generator.js.map +1 -1
- package/dist/cli/commands/api.d.ts +9 -0
- package/dist/cli/commands/api.d.ts.map +1 -1
- package/dist/cli/commands/api.js +10 -1
- package/dist/cli/commands/api.js.map +1 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/mcp-serve-context.d.ts.map +1 -1
- package/dist/cli/mcp-serve-context.js +40 -35
- package/dist/cli/mcp-serve-context.js.map +1 -1
- package/dist/cli/templates/resources.d.ts.map +1 -1
- package/dist/cli/templates/resources.js +5 -6
- package/dist/cli/templates/resources.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +37 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/data/field-encryption.d.ts +17 -0
- package/dist/data/field-encryption.d.ts.map +1 -0
- package/dist/data/field-encryption.js +80 -0
- package/dist/data/field-encryption.js.map +1 -0
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +2 -0
- package/dist/data/index.js.map +1 -1
- package/dist/data/mask-fields.d.ts +17 -0
- package/dist/data/mask-fields.d.ts.map +1 -0
- package/dist/data/mask-fields.js +56 -0
- package/dist/data/mask-fields.js.map +1 -0
- package/dist/data/registry.d.ts +4 -0
- package/dist/data/registry.d.ts.map +1 -1
- package/dist/data/registry.js +6 -0
- package/dist/data/registry.js.map +1 -1
- package/dist/data/repository.d.ts +3 -0
- package/dist/data/repository.d.ts.map +1 -1
- package/dist/data/repository.js +123 -41
- package/dist/data/repository.js.map +1 -1
- package/dist/define/defineCapability.d.ts +2 -0
- package/dist/define/defineCapability.d.ts.map +1 -1
- package/dist/define/defineCapability.js.map +1 -1
- package/dist/errors/data-errors.d.ts +23 -0
- package/dist/errors/data-errors.d.ts.map +1 -0
- package/dist/errors/data-errors.js +45 -0
- package/dist/errors/data-errors.js.map +1 -0
- package/dist/errors/index.d.ts +3 -11
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +3 -16
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/plumbus-error.d.ts +13 -0
- package/dist/errors/plumbus-error.d.ts.map +1 -0
- package/dist/errors/plumbus-error.js +15 -0
- package/dist/errors/plumbus-error.js.map +1 -0
- package/dist/execution/capability-executor.d.ts.map +1 -1
- package/dist/execution/capability-executor.js +142 -61
- package/dist/execution/capability-executor.js.map +1 -1
- package/dist/execution/capability-invocation.d.ts +3 -1
- package/dist/execution/capability-invocation.d.ts.map +1 -1
- package/dist/execution/capability-invocation.js +41 -2
- package/dist/execution/capability-invocation.js.map +1 -1
- package/dist/execution/context-deps.d.ts +23 -0
- package/dist/execution/context-deps.d.ts.map +1 -0
- package/dist/execution/context-deps.js +45 -0
- package/dist/execution/context-deps.js.map +1 -0
- package/dist/execution/context-factory.d.ts.map +1 -1
- package/dist/execution/context-factory.js +9 -1
- package/dist/execution/context-factory.js.map +1 -1
- package/dist/execution/deferred-flow-service.d.ts +9 -0
- package/dist/execution/deferred-flow-service.d.ts.map +1 -0
- package/dist/execution/deferred-flow-service.js +54 -0
- package/dist/execution/deferred-flow-service.js.map +1 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +2 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/transactional-outbox.d.ts +33 -0
- package/dist/execution/transactional-outbox.d.ts.map +1 -0
- package/dist/execution/transactional-outbox.js +84 -0
- package/dist/execution/transactional-outbox.js.map +1 -0
- package/dist/flows/engine.d.ts +1 -0
- package/dist/flows/engine.d.ts.map +1 -1
- package/dist/flows/engine.js +1 -1
- package/dist/flows/engine.js.map +1 -1
- package/dist/flows/flow-service.d.ts.map +1 -1
- package/dist/flows/flow-service.js +2 -2
- package/dist/flows/flow-service.js.map +1 -1
- package/dist/governance/index.d.ts +1 -1
- package/dist/governance/index.d.ts.map +1 -1
- package/dist/governance/index.js +1 -1
- package/dist/governance/index.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/jobs/deferred-job-dispatch-service.d.ts +7 -0
- package/dist/jobs/deferred-job-dispatch-service.d.ts.map +1 -0
- package/dist/jobs/deferred-job-dispatch-service.js +16 -0
- package/dist/jobs/deferred-job-dispatch-service.js.map +1 -0
- package/dist/jobs/dispatch.d.ts.map +1 -1
- package/dist/jobs/dispatch.js +8 -2
- package/dist/jobs/dispatch.js.map +1 -1
- package/dist/jobs/index.d.ts +3 -0
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +2 -0
- package/dist/jobs/index.js.map +1 -1
- package/dist/jobs/job-dispatch-service.d.ts +24 -0
- package/dist/jobs/job-dispatch-service.d.ts.map +1 -0
- package/dist/jobs/job-dispatch-service.js +26 -0
- package/dist/jobs/job-dispatch-service.js.map +1 -0
- package/dist/observability/index.d.ts +1 -1
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +1 -1
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/metrics.d.ts +4 -0
- package/dist/observability/metrics.d.ts.map +1 -1
- package/dist/observability/metrics.js +19 -1
- package/dist/observability/metrics.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts +2 -0
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +3 -1
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/register-consumers.d.ts.map +1 -1
- package/dist/runtime/register-consumers.js +12 -16
- package/dist/runtime/register-consumers.js.map +1 -1
- package/dist/runtime/start-worker-pool.d.ts.map +1 -1
- package/dist/runtime/start-worker-pool.js +5 -1
- package/dist/runtime/start-worker-pool.js.map +1 -1
- package/dist/runtime/startup-warnings.d.ts +8 -0
- package/dist/runtime/startup-warnings.d.ts.map +1 -0
- package/dist/runtime/startup-warnings.js +26 -0
- package/dist/runtime/startup-warnings.js.map +1 -0
- package/dist/server/bootstrap.d.ts +1 -1
- package/dist/server/bootstrap.d.ts.map +1 -1
- package/dist/server/bootstrap.js +49 -23
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/testing/e2e.d.ts.map +1 -1
- package/dist/testing/e2e.js +4 -1
- package/dist/testing/e2e.js.map +1 -1
- package/dist/translations/index.d.ts +2 -0
- package/dist/translations/index.d.ts.map +1 -1
- package/dist/translations/index.js +10 -0
- package/dist/translations/index.js.map +1 -1
- package/dist/types/capability.d.ts +5 -0
- package/dist/types/capability.d.ts.map +1 -1
- package/dist/types/config.d.ts +11 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/context.d.ts +37 -1
- package/dist/types/context.d.ts.map +1 -1
- package/dist/worker/bootstrap.d.ts.map +1 -1
- package/dist/worker/bootstrap.js +25 -37
- package/dist/worker/bootstrap.js.map +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +1 -1
- package/dist/worker/index.js.map +1 -1
- package/instructions/README.md +38 -0
- package/instructions/ai.md +68 -20
- package/instructions/api.md +2 -0
- package/instructions/capabilities.md +19 -1
- package/instructions/cli.md +68 -2
- package/instructions/events.md +2 -0
- package/instructions/framework.md +5 -1
- package/instructions/governance.md +2 -0
- package/instructions/patterns.md +1 -1
- package/instructions/peer-dependencies.md +17 -5
- package/instructions/prompts.md +84 -0
- package/instructions/security.md +27 -15
- package/instructions/testing.md +45 -32
- package/package.json +5 -4
package/instructions/cli.md
CHANGED
|
@@ -11,7 +11,8 @@ plumbus create <project-name> [--auth jwt|auth0|clerk] [--ai openai|anthropic] [
|
|
|
11
11
|
# Create a monorepo project (backend / frontend / libs)
|
|
12
12
|
plumbus create <project-name> --monorepo [--auth jwt|auth0|clerk] [--ai openai|anthropic] [--compliance GDPR|HIPAA] [--skip-install] [--git]
|
|
13
13
|
|
|
14
|
-
# Initialize AI agent wiring (
|
|
14
|
+
# Initialize AI agent wiring (all formats by default; use --agent to narrow)
|
|
15
|
+
plumbus init
|
|
15
16
|
plumbus init --agent copilot
|
|
16
17
|
|
|
17
18
|
# Check project health
|
|
@@ -83,8 +84,31 @@ plumbus generate
|
|
|
83
84
|
# Run governance checks (advisory signals, not hard blocks)
|
|
84
85
|
plumbus verify
|
|
85
86
|
plumbus verify --json
|
|
87
|
+
|
|
88
|
+
# Partner API contract validation (requires @plumbus/api)
|
|
89
|
+
plumbus api validate
|
|
90
|
+
plumbus api validate --fail-on-governance # also fail on advisory apiRules
|
|
91
|
+
|
|
92
|
+
# Compliance profile assessment
|
|
93
|
+
plumbus certify policy soc2
|
|
94
|
+
plumbus certify policy gdpr --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`plumbus verify` and `plumbus api validate` are both advisory for governance by default. Use `--fail-on-governance` on `api validate` when CI should gate on advisory API rules.
|
|
98
|
+
|
|
99
|
+
## Testing
|
|
100
|
+
|
|
101
|
+
Consumer apps run tests through the framework — do not install vitest directly.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
plumbus test
|
|
105
|
+
plumbus test --watch
|
|
106
|
+
plumbus test --all # unit tests, then e2e
|
|
107
|
+
plumbus e2e # Playwright browser suites
|
|
86
108
|
```
|
|
87
109
|
|
|
110
|
+
Full command reference: `docs/cli/commands.md` in the framework repo (or browse the published docs).
|
|
111
|
+
|
|
88
112
|
## Database Management
|
|
89
113
|
|
|
90
114
|
**All database operations must go through the framework. Never use manual SQL DDL queries.**
|
|
@@ -117,6 +141,10 @@ plumbus migrate push --create-db
|
|
|
117
141
|
|
|
118
142
|
# Rollback the last applied migration
|
|
119
143
|
plumbus migrate rollback
|
|
144
|
+
|
|
145
|
+
# Adopt migration history when the live schema already matches (drift recovery)
|
|
146
|
+
plumbus migrate reconcile
|
|
147
|
+
plumbus migrate reconcile --dry-run
|
|
120
148
|
```
|
|
121
149
|
|
|
122
150
|
All migration commands accept `--json` for machine-readable output.
|
|
@@ -125,7 +153,8 @@ All migration commands accept `--json` for machine-readable output.
|
|
|
125
153
|
1. Define entities in `app/entities/` using `defineEntity()`
|
|
126
154
|
2. Run `plumbus migrate generate` to produce SQL migration files in `drizzle/`
|
|
127
155
|
3. Run `plumbus migrate apply` to execute pending migrations
|
|
128
|
-
4.
|
|
156
|
+
4. If schema already exists but history is missing: `plumbus migrate reconcile` (use `--dry-run` to preview)
|
|
157
|
+
5. For rapid development, use `plumbus migrate push` to sync schema directly (no migration files)
|
|
129
158
|
|
|
130
159
|
**Never run `drizzle-kit` manually** — the framework wraps it programmatically.
|
|
131
160
|
|
|
@@ -147,6 +176,15 @@ plumbus start --port 8080 --host 127.0.0.1
|
|
|
147
176
|
|
|
148
177
|
Behind a load balancer set `TRUST_PROXY=true` (or a specific IP/CIDR) so Fastify trusts `X-Forwarded-*` headers. `app/server.ts` extension hooks (`onRoutesRegistered`, `resolveAiOverrides`, `onCapabilityError`, `onProcessError`, `onAICostRecorded`, `onFlowError`, `enableStrictStructuredOutputs`) are loaded automatically.
|
|
149
178
|
|
|
179
|
+
## Background work
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
plumbus worker start # split deployments
|
|
183
|
+
plumbus events status # outbox / queue health
|
|
184
|
+
plumbus flow dead-letter list # failed flow executions
|
|
185
|
+
plumbus seed # populate initial data
|
|
186
|
+
```
|
|
187
|
+
|
|
150
188
|
## Translations
|
|
151
189
|
|
|
152
190
|
```bash
|
|
@@ -205,6 +243,34 @@ export default async function (ctx: { sql: any; args: string[] }) {
|
|
|
205
243
|
}
|
|
206
244
|
```
|
|
207
245
|
|
|
246
|
+
## RAG ingestion
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
plumbus rag ingest <path> # Ingest a document into the RAG pipeline
|
|
250
|
+
plumbus rag ingest ./docs/guide.md --source support-docs --tenant-id <uuid>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Requires AI provider credentials in config/env. See `docs/ai/ai-integration.md` for corpus retrieval from capabilities.
|
|
254
|
+
|
|
255
|
+
## Framework upgrades
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
plumbus upgrade # Migrate legacy UI artifacts after @plumbus/core / @plumbus/ui bumps
|
|
259
|
+
plumbus upgrade --dry-run # Report stale artifacts without writing
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`plumbus doctor` suggests `plumbus upgrade` when it detects stale generated frontend layout.
|
|
263
|
+
|
|
264
|
+
## Agent brief sync
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
plumbus agent sync # Regenerate .plumbus/briefs/project.md from discovered contracts
|
|
268
|
+
plumbus agent brief capability getUser # One-off brief for a single resource
|
|
269
|
+
plumbus agent brief entity User
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Run `plumbus agent sync` after adding capabilities/entities so coding agents see an up-to-date project brief. `plumbus init` creates wiring files but skips refreshing an existing brief — use sync for that.
|
|
273
|
+
|
|
208
274
|
## Important Notes
|
|
209
275
|
|
|
210
276
|
- **Run from project root**: All commands must be executed from the consumer project directory where `package.json` lives.
|
package/instructions/events.md
CHANGED
|
@@ -47,6 +47,8 @@ handler: async (ctx, input) => {
|
|
|
47
47
|
|
|
48
48
|
Events are **not** dispatched immediately. They are written to an outbox table in the **same database transaction** as the capability's data changes. This guarantees at-least-once delivery with no data/event inconsistency.
|
|
49
49
|
|
|
50
|
+
For `action` / `eventHandler` capabilities this is the default (**transactional outbox ON**). Opt out with `execution.transactionalOutbox: false`, `PLUMBUS_TRANSACTIONAL_OUTBOX=false`, or per-capability `transactional: false`. Auto-excluded: `kind: 'job'`, `effects.ai: true`, non-empty `effects.external`. See `capabilities.md` § Transactional outbox.
|
|
51
|
+
|
|
50
52
|
A background dispatcher polls the outbox and publishes events to the queue system for delivery.
|
|
51
53
|
|
|
52
54
|
## Event Envelope
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Plumbus Framework
|
|
2
2
|
|
|
3
|
-
Plumbus is an AI-native, contract-driven TypeScript application framework. You build applications by composing six primitives — **Capabilities**, **Entities**, **Events**, **Flows**, **Prompts**, and **Translations** — through a controlled execution context (`ctx`).
|
|
3
|
+
Plumbus is an AI-native, contract-driven TypeScript application framework. You build applications by composing six primitives — **Capabilities**, **Entities**, **Events**, **Flows**, **Prompts**, and **Translations** — through a controlled execution context (`ctx`). Optional add-on packages extend the surface when installed explicitly: `@plumbus/api` (partner HTTP API), `@plumbus/mcp` (AI agent tools), `@plumbus/chat` (+ `@plumbus/chat-ui`), `@plumbus/voice`, `@plumbus/knowledge-base`, `@plumbus/ui` (web UI generation), and `@plumbus/browser-extension`.
|
|
4
4
|
|
|
5
5
|
## Non-Negotiable Guardrails
|
|
6
6
|
|
|
@@ -39,6 +39,7 @@ Every capability handler receives `ctx` — the scoped runtime context:
|
|
|
39
39
|
| `ctx.config` | Read-only application configuration |
|
|
40
40
|
| `ctx.security` | Security service — policy evaluation and access enforcement |
|
|
41
41
|
| `ctx.translations` | Localized string resolution — `ctx.translations.t("errors.notFound")` |
|
|
42
|
+
| `ctx.capabilities` | Cross-capability invocation — `ctx.capabilities.invoke('domain.name', input)` |
|
|
42
43
|
|
|
43
44
|
## Consumption Surfaces
|
|
44
45
|
|
|
@@ -47,6 +48,7 @@ Capabilities can be consumed by:
|
|
|
47
48
|
| Surface | Default | Opt-in |
|
|
48
49
|
|---------|---------|--------|
|
|
49
50
|
| **HTTP** (REST) | Auto-generated for every `query` / `action` / `job` capability | — |
|
|
51
|
+
| **Partner API** | Off | `exposeAs: ['api']` + `@plumbus/api` + `registerApiRoutes()` in `app/server.ts` |
|
|
50
52
|
| **Typed TS clients** (`@plumbus/ui`) | Generated by `plumbus ui generate` | — |
|
|
51
53
|
| **MCP** (Model Context Protocol) | Off | Add `exposeAs: ['mcp']` to selected capabilities; install `@plumbus/mcp`; run `plumbus mcp serve` |
|
|
52
54
|
|
|
@@ -143,6 +145,8 @@ The optional `app/server.ts` file exports hooks that customize server behavior.
|
|
|
143
145
|
| `onFlowError` | After a flow fails permanently (retries exhausted) | Logging flow failures to a system log table |
|
|
144
146
|
| `onProcessError` | On uncaught exceptions, unhandled rejections, and Fastify-level errors | Logging process-level crashes that bypass capability/flow hooks |
|
|
145
147
|
| `resolveAiOverrides` | Before each AI call | Dynamic model/provider configuration from DB |
|
|
148
|
+
| `onAICostRecorded` | After each AI cost record is written | Custom cost analytics or billing hooks |
|
|
149
|
+
| `enableStrictStructuredOutputs` | Server bootstrap | Global toggle for strict structured-output mode |
|
|
146
150
|
|
|
147
151
|
### Error Capture Coverage
|
|
148
152
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Plumbus governance is **advisory, not blocking** — "guardrails, not gates." Rules produce warnings that inform developers; they don't prevent deployment.
|
|
4
4
|
|
|
5
|
+
The same model applies to `plumbus api validate`: governance signals print to stderr but do not fail the command unless you pass `--fail-on-governance`. Hard contract findings (manifest, policy, path params, fixtures) still exit `1` without that flag.
|
|
6
|
+
|
|
5
7
|
## Governance Signals
|
|
6
8
|
|
|
7
9
|
```ts
|
package/instructions/patterns.md
CHANGED
|
@@ -17,7 +17,7 @@ These rules are non-negotiable. They are architecture constraints, not style pre
|
|
|
17
17
|
| Event | domain.pastTense | `order.placed`, `refund.requested`, `user.updated` |
|
|
18
18
|
| Flow | camelCase noun | `refundApproval`, `orderFulfillment` |
|
|
19
19
|
| Prompt | camelCase verb+noun | `summarizeTicket`, `classifyIntent` |
|
|
20
|
-
| Domain | lowercase
|
|
20
|
+
| Domain | lowercase, one word (plural is common) | `users`, `billing`, `fulfillment` |
|
|
21
21
|
|
|
22
22
|
## File Structure
|
|
23
23
|
|
|
@@ -4,27 +4,39 @@ Read this file **before** editing `peerDependencies` in any `packages/*/package.
|
|
|
4
4
|
|
|
5
5
|
## CRITICAL rules
|
|
6
6
|
|
|
7
|
-
1. **Copy literals — do not derive ranges.** Never compute semver ranges from intuition. Copy the exact string from the table below or from a canonical `package.json` (`packages/mcp/package.json`, `packages/api/package.json`).
|
|
7
|
+
1. **Copy literals — do not derive ranges.** Never compute semver ranges from intuition. Copy the exact string from the table below or from a canonical `package.json` (`packages/mcp/package.json`, `packages/api/package.json`, `packages/voice/package.json`).
|
|
8
8
|
2. **Never use `^0.x` caret ranges** on `@plumbus/core` peers in add-on packages. npm treats `^0.5.0` as **0.5.x only** (`>=0.5.0 <0.6.0`), not "0.5 and above."
|
|
9
9
|
3. **pnpm passing locally does not prove peers are correct.** Backend Docker images install production deps with **npm** (`deployment.md` Rule 7). Wrong peers break Docker builds even when `pnpm install` succeeds.
|
|
10
10
|
4. **Do not copy from CHANGELOG history.** Older releases documented incorrect ranges (`^0.5.0 <0.7.0` claimed to support 0.6.x — it does not under npm). Use this file and the canonical `package.json` files instead.
|
|
11
11
|
|
|
12
|
-
## Canonical `@plumbus/core` peer strings
|
|
12
|
+
## Canonical `@plumbus/core` peer strings (add-ons → core)
|
|
13
13
|
|
|
14
14
|
| Package kind | `peerDependencies["@plumbus/core"]` | Canonical copy-from |
|
|
15
15
|
|---|---|---|
|
|
16
16
|
| Most add-ons (chat, chat-ui, knowledge-base, mcp, api, browser-extension) | `"0.5.x \|\| 0.6.x"` | `packages/mcp/package.json` |
|
|
17
|
-
| Voice only (requires core 0.6+ media/cost APIs) | `"
|
|
17
|
+
| Voice only (requires core 0.6+ media/cost APIs) | `"0.6.x"` | `packages/voice/package.json` |
|
|
18
18
|
|
|
19
19
|
When adding a **new** publishable add-on under `packages/`, use `"0.5.x || 0.6.x"` unless the package genuinely requires core 0.6+ only (then use the voice pattern).
|
|
20
20
|
|
|
21
|
+
## Other publishable peer strings
|
|
22
|
+
|
|
23
|
+
| Declaring package | Peer target | Literal | Required? | Canonical copy-from |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| `@plumbus/core` | `@plumbus/mcp` | `"0.5.x \|\| 0.6.x"` | optional | `packages/plumbus-core/package.json` |
|
|
26
|
+
| `@plumbus/core` | `@plumbus/api` | `"0.1.x"` | optional | `packages/plumbus-core/package.json` |
|
|
27
|
+
| `@plumbus/chat` | `@plumbus/knowledge-base` | `"^0.1.0"` | optional | `packages/chat/package.json` |
|
|
28
|
+
| `@plumbus/chat-ui` | `@plumbus/chat` | `"0.1.x"` | required | `packages/chat-ui/package.json` |
|
|
29
|
+
| `@plumbus/chat-ui` | `@plumbus/core` | `"0.5.x \|\| 0.6.x"` | required | `packages/chat-ui/package.json` |
|
|
30
|
+
|
|
31
|
+
**Peering direction:** add-ons declare `@plumbus/core` as a peer — consumer apps install both. `@plumbus/core` optionally peers `@plumbus/mcp` and `@plumbus/api` when those packages are present. `@plumbus/chat` optionally peers `@plumbus/knowledge-base` for registry-backed context sources — **not** the reverse. `@plumbus/knowledge-base` only peers `@plumbus/core`.
|
|
32
|
+
|
|
21
33
|
## Forbidden patterns
|
|
22
34
|
|
|
23
35
|
| Range | Why it is wrong |
|
|
24
36
|
|---|---|
|
|
25
37
|
| `"^0.5.0 <0.7.0"` | npm resolves `^0.5.0` to 0.5.x only — **rejects `@plumbus/core@0.6.0`** |
|
|
26
38
|
| `"^0.5.0 <0.6.0"` | 0.5.x only — rejects 0.6.x |
|
|
27
|
-
| `"^0.6.
|
|
39
|
+
| `"^0.6.0 <0.7.0"` or `"^0.6.x"` on voice | Use the voice literal `"0.6.x"` from `packages/voice/package.json` |
|
|
28
40
|
| Widening an upper bound on a caret range | e.g. changing `<0.6.0` to `<0.7.0` on `^0.5.0` does **not** add 0.6.x support |
|
|
29
41
|
|
|
30
42
|
## When `@plumbus/core` gets a new minor line (e.g. 0.7.0)
|
|
@@ -43,4 +55,4 @@ See also: `.agents/skills/bump-version/SKILL.md` (core **minor** bump checklist)
|
|
|
43
55
|
|
|
44
56
|
- `deployment.md` Rule 7 — backend `proddeps` uses `npm install --omit=dev`
|
|
45
57
|
- `.agents/skills/new-package-instructions/SKILL.md` — new package `package.json` peers
|
|
46
|
-
- `packages/mcp/package.json` / `packages/api/package.json` — canonical examples
|
|
58
|
+
- `packages/mcp/package.json` / `packages/api/package.json` / `packages/voice/package.json` — canonical examples
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Prompts
|
|
2
|
+
|
|
3
|
+
Prompts are structured AI interaction templates defined with `definePrompt()`. They pair typed Zod `input`/`output` schemas with prompt text and model tuning.
|
|
4
|
+
|
|
5
|
+
## Prompt content
|
|
6
|
+
|
|
7
|
+
The actual instructions sent to the provider live in two optional string fields:
|
|
8
|
+
|
|
9
|
+
| Field | Purpose |
|
|
10
|
+
|-------|---------|
|
|
11
|
+
| `system` | Stable provider system instructions (role, rules, output format) |
|
|
12
|
+
| `description` | User/data prompt content (the main task text) |
|
|
13
|
+
|
|
14
|
+
Both support simple top-level `{{key}}` substitution from the prompt `input` object.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { definePrompt } from "@plumbus/core";
|
|
18
|
+
import { z } from "@plumbus/core/zod";
|
|
19
|
+
|
|
20
|
+
export const classifyTicket = definePrompt({
|
|
21
|
+
name: "classifyTicket",
|
|
22
|
+
domain: "support",
|
|
23
|
+
system: "You classify support tickets. Return only the requested JSON fields.",
|
|
24
|
+
description: "Ticket text:\n{{ticketText}}\n\nCustomer tier: {{customerTier}}",
|
|
25
|
+
input: z.object({
|
|
26
|
+
ticketText: z.string(),
|
|
27
|
+
customerTier: z.enum(["free", "pro", "enterprise"]).optional(),
|
|
28
|
+
}),
|
|
29
|
+
output: z.object({
|
|
30
|
+
category: z.enum(["billing", "technical", "general"]),
|
|
31
|
+
priority: z.enum(["low", "medium", "high"]),
|
|
32
|
+
}),
|
|
33
|
+
model: {
|
|
34
|
+
temperature: 0.2,
|
|
35
|
+
maxTokens: 300,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
When `system` is omitted, the framework uses the prior single user-message behavior (description only).
|
|
41
|
+
|
|
42
|
+
### Unsubstituted input
|
|
43
|
+
|
|
44
|
+
By default, any input keys not referenced as `{{key}}` in `system`/`description` are appended to the user message. Set `appendUnsubstitutedInput: false` to disable that behavior.
|
|
45
|
+
|
|
46
|
+
## Model configuration
|
|
47
|
+
|
|
48
|
+
`model` is an optional `ModelConfig` object:
|
|
49
|
+
|
|
50
|
+
| Field | Type | Notes |
|
|
51
|
+
|-------|------|-------|
|
|
52
|
+
| `provider` | string? | Routes to a named provider in multi-provider config |
|
|
53
|
+
| `name` | string? | Model id (e.g. `gpt-4o-mini`) |
|
|
54
|
+
| `temperature` | number? | Sampling temperature |
|
|
55
|
+
| `maxTokens` | number? | Max output tokens |
|
|
56
|
+
|
|
57
|
+
**Recommended convention:** omit `provider` and `name` in the prompt definition and resolve them via env (`AI_DEFAULT_PROVIDER`, `AI_DEFAULT_MODEL`, `PROMPT_{NAME}_*`) or the `resolveAiOverrides` hook in `app/server.ts`. Set only `temperature` and `maxTokens` in the prompt when you want per-prompt tuning baked into the contract.
|
|
58
|
+
|
|
59
|
+
Resolution order: `resolveAiOverrides` → per-prompt env vars → prompt `model` fields → `AI_DEFAULT_MODEL` / `AI_DEFAULT_PROVIDER`.
|
|
60
|
+
|
|
61
|
+
## Structured output and streaming flags
|
|
62
|
+
|
|
63
|
+
| Field | Effect |
|
|
64
|
+
|-------|--------|
|
|
65
|
+
| `disableStrictStructuredOutputs` | Opt out of strict JSON-schema mode for this prompt |
|
|
66
|
+
| `requireStrictStructuredOutputs` | Refuse to run unless a provider JSON Schema can be sent (no silent prompt-only JSON fallback) |
|
|
67
|
+
| `structuredOutputTransport` | How structured output is requested from the provider |
|
|
68
|
+
| `skipStreamValidationFallback` | On `streamGenerate`, do not fall back to non-streaming retry on validation failure |
|
|
69
|
+
| `disableTextModeBrevityHint` | Skip the automatic brevity hint for text-mode prompts |
|
|
70
|
+
|
|
71
|
+
See `docs/ai/ai-integration.md` for strict structured outputs and streaming behavior.
|
|
72
|
+
|
|
73
|
+
## Invoking prompts
|
|
74
|
+
|
|
75
|
+
From capabilities, use `ctx.ai`:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const result = await ctx.ai.generate({
|
|
79
|
+
prompt: "classifyTicket",
|
|
80
|
+
input: { ticketText: input.body, customerTier: "enterprise" },
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Also available: `generateWithUsage` (returns tokens + cost), `streamGenerate` (async iterator), `extract`, `classify`, `retrieve`. See `ai.md` for the full `ctx.ai` surface.
|
package/instructions/security.md
CHANGED
|
@@ -15,12 +15,14 @@ access: {
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### Evaluation Order
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
|
|
19
|
+
1. If no `access` policy → **deny**
|
|
20
|
+
2. If `public: true` → allow
|
|
21
|
+
3. Authenticate identity → populate `ctx.auth` (`userId` required for non-public)
|
|
22
|
+
4. If `serviceAccounts` is set and `auth.userId` matches → **allow** (short-circuit; skips roles, scopes, and tenant checks)
|
|
23
|
+
5. If `tenantScoped: true` on the capability → caller must have `tenantId` in `ctx.auth`
|
|
24
|
+
6. Check `roles` — user must have at least one matching role (when `roles` is set)
|
|
25
|
+
7. Check `scopes` — user must have all required scopes (when `scopes` is set)
|
|
24
26
|
|
|
25
27
|
If any check fails → **403 Forbidden** with audit record.
|
|
26
28
|
|
|
@@ -47,17 +49,22 @@ If flow steps return **403** after upgrading to 0.5.x, audit `access.roles` on s
|
|
|
47
49
|
|
|
48
50
|
## Tenant Isolation
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
52
|
+
**Entity-level filtering:** when an entity declares `tenantScoped: true`, repository queries automatically inject `WHERE tenantId = ctx.auth.tenantId`. Cross-tenant data access is blocked at the framework level.
|
|
53
|
+
|
|
54
|
+
**Capability-level gate:** the capability's `access.tenantScoped` flag requires the caller to carry a `tenantId` in `ctx.auth`. It does not turn on repository filtering by itself — that follows the entity definition.
|
|
55
|
+
|
|
56
|
+
**Cross-tenant admin bypass:** capabilities with `access.tenantScoped: false` pass `bypassTenantScope: true` into `createDependencies()`, allowing explicitly cross-tenant admin routes while role/scope checks still apply.
|
|
57
|
+
|
|
58
|
+
Events carry `tenantId` and are only delivered to matching consumers.
|
|
54
59
|
|
|
55
60
|
## Field Classification & Edit Zones
|
|
56
61
|
|
|
57
62
|
Entity fields with `classification: "sensitive"` or `"highly_sensitive"`:
|
|
58
63
|
- Are masked in logs and audit records
|
|
59
64
|
- Trigger governance warnings if used in AI prompts
|
|
60
|
-
- Can be flagged for encryption at rest
|
|
65
|
+
- Can be flagged for encryption at rest (`encrypted: true` + `PLUMBUS_ENCRYPTION_KEY`)
|
|
66
|
+
|
|
67
|
+
**AI prompt scanning** is separate and **opt-in**: configure `aiProviders.security` (or `AI_SECURITY_*` env) to warn/redact/block classified fields in `ctx.ai.*` inputs. Without that config, classification alone does not scan prompts. See `ai.md` § Security.
|
|
61
68
|
|
|
62
69
|
### Edit Zones
|
|
63
70
|
|
|
@@ -69,11 +76,16 @@ Entity fields with `classification: "sensitive"` or `"highly_sensitive"`:
|
|
|
69
76
|
|
|
70
77
|
## Authentication Adapters
|
|
71
78
|
|
|
72
|
-
Plumbus uses pluggable auth adapters. The
|
|
79
|
+
Plumbus uses pluggable auth adapters. The adapter normalizes tokens into `AuthContext`:
|
|
80
|
+
|
|
81
|
+
- **JWT (default)** — `createJwtAdapter()` verifies HMAC-SHA256 tokens (`secret`, optional `issuer`/`audience`, `claimMapping`)
|
|
82
|
+
- **OIDC** — `createOidcAdapter()` verifies RS256/ES256 via JWKS (`issuer`, `audience`, optional `jwksUri`/`jwksCacheTtl`)
|
|
83
|
+
- **SAML 2.0** — `createSamlAdapter()` validates SAML assertions (`idpCertificate`, `issuer`, `audience`)
|
|
84
|
+
- **SCIM 2.0 provisioning** — `createScimService()` for IdP-driven user lifecycle (`createUser`, `patchUser`, etc.)
|
|
85
|
+
- **JWT signing** — `signJwt()` for first-party token issuance
|
|
86
|
+
- **Custom** — implement the `AuthAdapter` interface
|
|
73
87
|
|
|
74
|
-
-
|
|
75
|
-
- Auth0, Clerk, Cognito — provider-specific adapters (community)
|
|
76
|
-
- Custom — implement the adapter interface
|
|
88
|
+
See `docs/security/security-model.md` for full adapter examples.
|
|
77
89
|
|
|
78
90
|
## Password Utilities
|
|
79
91
|
|
package/instructions/testing.md
CHANGED
|
@@ -15,15 +15,19 @@ import { runCapability, simulateFlow, mockAI, createTestContext } from "@plumbus
|
|
|
15
15
|
Execute a capability in an isolated test environment:
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
const result = await runCapability(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const result = await runCapability(
|
|
19
|
+
getUser,
|
|
20
|
+
{ userId: "usr_123" },
|
|
21
|
+
{
|
|
22
|
+
auth: { userId: "admin_1", roles: ["admin"], scopes: [], provider: "test" },
|
|
23
|
+
data: {
|
|
24
|
+
User: [{ id: "usr_123", name: "Alice", email: "alice@test.com" }],
|
|
25
|
+
},
|
|
23
26
|
},
|
|
24
|
-
|
|
27
|
+
);
|
|
25
28
|
|
|
26
|
-
expect(result.
|
|
29
|
+
expect(result.success).toBe(true);
|
|
30
|
+
expect(result.data?.name).toBe("Alice");
|
|
27
31
|
```
|
|
28
32
|
|
|
29
33
|
### Paginated list capabilities
|
|
@@ -37,8 +41,10 @@ When testing `kind: 'query'` list endpoints that paginate via `findMany` + `coun
|
|
|
37
41
|
```ts
|
|
38
42
|
const page1 = await runCapability(listStaff, { page: 1, limit: 2 }, { ctx });
|
|
39
43
|
const page2 = await runCapability(listStaff, { page: 2, limit: 2 }, { ctx });
|
|
40
|
-
expect(page1.
|
|
41
|
-
expect(
|
|
44
|
+
expect(page1.success && page1.data).toBeTruthy();
|
|
45
|
+
expect(page2.success && page2.data).toBeTruthy();
|
|
46
|
+
expect(page1.data.items).not.toEqual(page2.data.items);
|
|
47
|
+
expect(page1.data.total).toBeGreaterThan(page1.data.limit);
|
|
42
48
|
```
|
|
43
49
|
|
|
44
50
|
### `simulateFlow`
|
|
@@ -46,16 +52,21 @@ expect(page1.total).toBeGreaterThan(page1.limit);
|
|
|
46
52
|
Simulate flow execution and inspect step history:
|
|
47
53
|
|
|
48
54
|
```ts
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
import { refundApproval } from "../refundApproval/flow.js";
|
|
56
|
+
|
|
57
|
+
const execution = await simulateFlow(
|
|
58
|
+
refundApproval,
|
|
59
|
+
{ refundId: "rf_1", amount: 50 },
|
|
60
|
+
{
|
|
61
|
+
capabilityResults: {
|
|
62
|
+
"billing.validateRefund": { valid: true },
|
|
63
|
+
},
|
|
53
64
|
},
|
|
54
|
-
|
|
65
|
+
);
|
|
55
66
|
|
|
56
67
|
expect(execution.status).toBe("completed");
|
|
57
|
-
expect(execution.
|
|
58
|
-
expect(execution.
|
|
68
|
+
expect(execution.history.length).toBeGreaterThan(0);
|
|
69
|
+
expect(execution.history[0]?.stepName).toBe("validateRefund");
|
|
59
70
|
```
|
|
60
71
|
|
|
61
72
|
### `mockAI`
|
|
@@ -65,7 +76,7 @@ Mock AI provider responses:
|
|
|
65
76
|
```ts
|
|
66
77
|
const ctx = createTestContext({
|
|
67
78
|
ai: mockAI({
|
|
68
|
-
|
|
79
|
+
generate: { summary: "Customer wants refund", priority: "high", sentiment: "negative" },
|
|
69
80
|
}),
|
|
70
81
|
});
|
|
71
82
|
|
|
@@ -73,6 +84,8 @@ const result = await ctx.ai.generate({ prompt: "summarizeTicket", input: { ticke
|
|
|
73
84
|
expect(result.priority).toBe("high");
|
|
74
85
|
```
|
|
75
86
|
|
|
87
|
+
`mockAI` keys responses by **operation** (`generate`, `extract`, `classify`, `retrieve`), not by prompt name. All prompts share the configured response for each operation.
|
|
88
|
+
|
|
76
89
|
### `createTestContext`
|
|
77
90
|
|
|
78
91
|
Build a test `ctx` with controllable mocks:
|
|
@@ -101,21 +114,21 @@ const execution = await simulateFlow(myFlow, input, {
|
|
|
101
114
|
## Security Test Patterns
|
|
102
115
|
|
|
103
116
|
```ts
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
117
|
+
import { assertCapabilityDenied } from "@plumbus/core/testing";
|
|
118
|
+
|
|
119
|
+
// Assert unauthorized access is rejected (returns { success: false }, does not throw)
|
|
120
|
+
const denied = await runCapability(
|
|
121
|
+
deleteUser,
|
|
122
|
+
{ userId: "usr_1" },
|
|
123
|
+
{ auth: { roles: ["viewer"], scopes: [], provider: "test" } },
|
|
124
|
+
);
|
|
125
|
+
expect(denied.success).toBe(false);
|
|
126
|
+
expect(denied.error?.code).toBe("forbidden");
|
|
127
|
+
|
|
128
|
+
// Or use the helper
|
|
129
|
+
await assertCapabilityDenied(deleteUser, { userId: "usr_1" }, {
|
|
130
|
+
auth: { roles: ["viewer"], scopes: [], provider: "test" },
|
|
131
|
+
});
|
|
119
132
|
```
|
|
120
133
|
|
|
121
134
|
## Governance Test Patterns
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumbus/core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Plumbus framework core — types, SDK, runtime, CLI, test utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
35
|
-
"instructions"
|
|
35
|
+
"instructions",
|
|
36
|
+
"CHANGELOG.md"
|
|
36
37
|
],
|
|
37
38
|
"keywords": [
|
|
38
39
|
"plumbus",
|
|
@@ -95,8 +96,8 @@
|
|
|
95
96
|
"esbuild": "^0.25.12",
|
|
96
97
|
"typescript": "^5.7.0",
|
|
97
98
|
"@plumbus/api": "0.1.2",
|
|
98
|
-
"@plumbus/
|
|
99
|
-
"@plumbus/
|
|
99
|
+
"@plumbus/voice": "0.3.0",
|
|
100
|
+
"@plumbus/mcp": "0.5.1"
|
|
100
101
|
},
|
|
101
102
|
"scripts": {
|
|
102
103
|
"build": "tsc -b",
|