@plumbus/core 0.6.3 → 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/define/defineTranslation.d.ts +39 -3
- package/dist/define/defineTranslation.d.ts.map +1 -1
- package/dist/define/defineTranslation.js +3 -0
- package/dist/define/defineTranslation.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/instructions/translations.md +21 -2
- package/package.json +6 -5
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @plumbus/core — Agent Instructions
|
|
2
|
+
|
|
3
|
+
This folder ships with the npm tarball. It is the entry point for AI coding agents working in a Plumbus consumer app. Read these files when implementing framework primitives, CLI workflows, security, testing, or optional add-on integration.
|
|
4
|
+
|
|
5
|
+
For conceptual reference, see `docs/` in the Plumbus monorepo. These files are **prescriptive** (do this, don't do that).
|
|
6
|
+
|
|
7
|
+
| File | When to read |
|
|
8
|
+
|------|--------------|
|
|
9
|
+
| [guardrails.md](./guardrails.md) | First. Framework-first rules, forbidden escape hatches, git safety. |
|
|
10
|
+
| [framework.md](./framework.md) | Core abstractions, `ctx`, consumption surfaces, project layout, server hooks. |
|
|
11
|
+
| [capabilities.md](./capabilities.md) | `defineCapability()`, kinds, access, effects, MCP/API exposure. |
|
|
12
|
+
| [entities.md](./entities.md) | `defineEntity()`, fields, classification, tenant scoping. |
|
|
13
|
+
| [events.md](./events.md) | `defineEvent()`, outbox pattern, consumers. |
|
|
14
|
+
| [flows.md](./flows.md) | `defineFlow()`, steps, triggers, retries. |
|
|
15
|
+
| [prompts.md](./prompts.md) | `definePrompt()`, `system`/`description`, model resolution. |
|
|
16
|
+
| [ai.md](./ai.md) | `ctx.ai` operations, cost tracking, env-based provider config. |
|
|
17
|
+
| [translations.md](./translations.md) | `defineTranslation()`, `ctx.translations`. |
|
|
18
|
+
| [security.md](./security.md) | Access policies, tenant isolation, auth adapters. |
|
|
19
|
+
| [governance.md](./governance.md) | Advisory rules, `plumbus verify`, compliance profiles. |
|
|
20
|
+
| [testing.md](./testing.md) | `runCapability`, `simulateFlow`, `mockAI`, security asserts. |
|
|
21
|
+
| [patterns.md](./patterns.md) | Naming conventions, do's/don'ts, common recipes. |
|
|
22
|
+
| [cli.md](./cli.md) | CLI commands (dev, migrate, generate, test, etc.). |
|
|
23
|
+
| [mcp.md](./mcp.md) | Core MCP surface (`exposeAs`, `plumbus mcp *`). |
|
|
24
|
+
| [api.md](./api.md) | Core partner API surface (`exposeAs: ['api']`). |
|
|
25
|
+
| [deployment.md](./deployment.md) | Production deployment, workers, health, env vars. |
|
|
26
|
+
| [peer-dependencies.md](./peer-dependencies.md) | **Before editing add-on `peerDependencies`** — copy literals exactly. |
|
|
27
|
+
| [upgrading-0.5-capabilities.md](./upgrading-0.5-capabilities.md) | Canonical capability names, invoke policy, flow auth snapshot. |
|
|
28
|
+
|
|
29
|
+
Package quickstart: [../README.md](../README.md).
|
|
30
|
+
|
|
31
|
+
## Critical rules
|
|
32
|
+
|
|
33
|
+
- **Primitives are mandatory architecture** — business logic belongs in capabilities, flows, entities, events, prompts, and translations.
|
|
34
|
+
- **Use `ctx.*` subsystems** — do not bypass the framework with ad hoc infrastructure unless documented.
|
|
35
|
+
- **Never edit `.plumbus/generated/`** — regenerate with `plumbus generate`.
|
|
36
|
+
- **Do not install framework-provided deps** — import Zod from `@plumbus/core/zod`; run tests with `plumbus test`.
|
|
37
|
+
- **Mock AI by operation** — `mockAI({ generate, extract, classify, retrieve })`, not by prompt name.
|
|
38
|
+
- **MCP tool names are canonical** — `<domain>.<name>` (e.g. `billing.getRefund`).
|
package/instructions/ai.md
CHANGED
|
@@ -27,7 +27,7 @@ export const summarizeTicket = definePrompt({
|
|
|
27
27
|
});
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Prompts should NOT hardcode `provider` or model `name
|
|
30
|
+
Prompts should NOT hardcode `provider` or model `name` unless you have a deliberate per-prompt override. Prefer env-based resolution (`AI_DEFAULT_*`, `PROMPT_{NAME}_*`) or `resolveAiOverrides` in `app/server.ts`. Only set `temperature` and `maxTokens` in the prompt definition when tuning is part of the contract. See `prompts.md` for `system`/`description` content.
|
|
31
31
|
|
|
32
32
|
## `ctx.ai` Operations
|
|
33
33
|
|
|
@@ -74,15 +74,63 @@ const docs = await ctx.ai.retrieve({ query: "refund policy for enterprise custom
|
|
|
74
74
|
// docs: Array<{ content: string, source: string, score: number, metadata?: Record<string, unknown> }>
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
Ingest documents with `plumbus rag ingest <path>` (see `cli.md`).
|
|
78
|
+
|
|
79
|
+
### Generate With Usage
|
|
80
|
+
|
|
81
|
+
Returns validated output plus token counts and per-request cost:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const { data, usage, model, provider, cost } = await ctx.ai.generateWithUsage({
|
|
85
|
+
prompt: "summarizeTicket",
|
|
86
|
+
input: { ticketText: input.text, customerTier: "enterprise" },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Stream Generate
|
|
91
|
+
|
|
92
|
+
Stream partial output for long-running generations:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
for await (const event of ctx.ai.streamGenerate({
|
|
96
|
+
prompt: "writeChapter",
|
|
97
|
+
input: { topic: input.topic },
|
|
98
|
+
})) {
|
|
99
|
+
if (event.type === "delta") process.stdout.write(event.text);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Multi-turn messages
|
|
104
|
+
|
|
105
|
+
Pass explicit chat messages instead of a named prompt:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await ctx.ai.generate({
|
|
109
|
+
messages: [
|
|
110
|
+
{ role: "system", content: "You are a helpful assistant." },
|
|
111
|
+
{ role: "user", content: input.question },
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Cost helpers
|
|
117
|
+
|
|
118
|
+
- `ctx.ai.checkProviderCostBudget()` — pre-flight budget check before provider calls
|
|
119
|
+
- `ctx.ai.recordProviderCost()` — record spend from external provider usage APIs
|
|
120
|
+
|
|
77
121
|
## Output Validation
|
|
78
122
|
|
|
79
123
|
All AI responses are validated against the prompt's output Zod schema. On failure, the framework retries generation (configurable). If retries are exhausted, a structured error is raised.
|
|
80
124
|
|
|
81
125
|
## Cost Tracking
|
|
82
126
|
|
|
83
|
-
Every AI invocation records: model used, input/output token counts, latency, and prompt name. Token counts come directly from provider responses
|
|
127
|
+
Every AI invocation records: model used, input/output token counts, latency, and prompt name. Token counts come directly from provider responses.
|
|
128
|
+
|
|
129
|
+
**Per-request cost** is computed automatically from a built-in pricing table (`calculateModelCost`) covering major OpenAI and Anthropic models. Unknown models (Ollama, custom endpoints) return cost `$0`. Cached-token and long-context adjustments are applied when providers return cache metadata.
|
|
84
130
|
|
|
85
|
-
|
|
131
|
+
`generateWithUsage()` and `streamGenerate()` expose `cost` on each call. Budget limits (`BudgetConfig`) can gate requests by tokens or dollar amounts.
|
|
132
|
+
|
|
133
|
+
**Optional billing reconciliation:** use `createUsageAPIClient()` + `createCostTracker().syncCosts()` to pull actual invoices from provider billing APIs — this supplements, but does not replace, per-request cost from the pricing table:
|
|
86
134
|
|
|
87
135
|
```ts
|
|
88
136
|
import { createUsageAPIClient, createCostTracker } from "@plumbus/core";
|
|
@@ -92,24 +140,10 @@ const openaiUsage = createUsageAPIClient({
|
|
|
92
140
|
apiKey: process.env.AI_OPENAI_API_KEY,
|
|
93
141
|
});
|
|
94
142
|
|
|
95
|
-
const
|
|
96
|
-
provider: "anthropic",
|
|
97
|
-
apiKey: process.env.AI_ANTHROPIC_API_KEY,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const costTracker = createCostTracker(budgetConfig, [openaiUsage, anthropicUsage]);
|
|
101
|
-
|
|
102
|
-
// Fetch actual costs from billing APIs
|
|
143
|
+
const costTracker = createCostTracker(budgetConfig, [openaiUsage]);
|
|
103
144
|
const result = await costTracker.syncCosts();
|
|
104
|
-
if (!result.synced) {
|
|
105
|
-
console.warn("Cost sync failed:", result.error);
|
|
106
|
-
}
|
|
107
145
|
```
|
|
108
146
|
|
|
109
|
-
If the usage API is not configured or unavailable, `cost` on each record is `null` and dollar-based budget limits cannot be enforced. Token-based budget limits always work.
|
|
110
|
-
|
|
111
|
-
Budget limits (per-request token limit, daily cost limit, per-tenant daily limit) are enforced via `BudgetConfig`.
|
|
112
|
-
|
|
113
147
|
## Configuration via Environment Variables
|
|
114
148
|
|
|
115
149
|
Use `loadConfig()` from `@plumbus/core` in your `config/app.config.ts` — it reads AI provider settings from env vars automatically.
|
|
@@ -125,7 +159,7 @@ AI_MODEL=gpt-4o-mini
|
|
|
125
159
|
|
|
126
160
|
### Multi-Provider
|
|
127
161
|
|
|
128
|
-
Set `AI_DEFAULT_PROVIDER` to enable multi-provider mode.
|
|
162
|
+
Set `AI_DEFAULT_PROVIDER` to enable multi-provider mode. Env-based discovery supports **only** `AI_OPENAI_*` and `AI_ANTHROPIC_*` slots — other `AI_{NAME}_API_KEY` values are ignored with a warning. Wire additional providers programmatically via `createProviderAdapter` / `createAIService`.
|
|
129
163
|
|
|
130
164
|
```bash
|
|
131
165
|
AI_DEFAULT_PROVIDER=openai
|
|
@@ -204,6 +238,20 @@ The framework calls this hook before each `generate()`, `generateWithUsage()`, a
|
|
|
204
238
|
|
|
205
239
|
## Security
|
|
206
240
|
|
|
207
|
-
-
|
|
241
|
+
Classified-field scanning is **opt-in**. It runs only when `aiProviders.security` is set in config or via `AI_SECURITY_*` env vars. Omit the block entirely to leave AI calls unscanned (capability `access` policies still apply).
|
|
242
|
+
|
|
243
|
+
When configured:
|
|
244
|
+
|
|
245
|
+
| Mode | Behavior |
|
|
246
|
+
|------|----------|
|
|
247
|
+
| `redact` (default) | Warn at `warnThreshold`; replace fields at/above `redactThreshold` with `[REDACTED]` and continue |
|
|
248
|
+
| `block` | Abort the AI call when any field at/above `warnThreshold` is detected (`AISecurityBlockedError`) |
|
|
249
|
+
|
|
250
|
+
Entity definitions are merged from the registry when the `security` block is present. Thresholds: `warnThreshold` / `redactThreshold` on config, or `AI_SECURITY_WARN_THRESHOLD` / `AI_SECURITY_REDACT_THRESHOLD`. Invalid `AI_SECURITY_MODE` values warn at load and fall back to `redact`.
|
|
251
|
+
|
|
252
|
+
Also:
|
|
253
|
+
|
|
208
254
|
- Tenant isolation is enforced — prompts cannot access cross-tenant data
|
|
209
255
|
- All AI invocations are recorded in the audit trail
|
|
256
|
+
|
|
257
|
+
See `docs/ai/ai-integration.md` and `docs/upgrading-contract-alignment.md` §12.
|
package/instructions/api.md
CHANGED
|
@@ -16,4 +16,6 @@ Plumbus capabilities can be exposed to partners on versioned HTTP routes. **Cont
|
|
|
16
16
|
- **`plumbus api validate` / `generate` / `diff` / `test-fixtures validate`** — CLI entry points; dynamic import of `@plumbus/api`.
|
|
17
17
|
- **`registerApiRoutes()`** — mount partner routes at app bootstrap (requires `@plumbus/api` install).
|
|
18
18
|
|
|
19
|
+
`plumbus api validate` exits `1` only on hard findings by default; pass `--fail-on-governance` to also fail on advisory API governance signals. Details: `node_modules/@plumbus/api/instructions/manifest-and-cli.md`.
|
|
20
|
+
|
|
19
21
|
Install the runtime when serving partners: `pnpm add @plumbus/api` (optional peer of `@plumbus/core`, version-locked `0.1.x`).
|
|
@@ -119,6 +119,22 @@ Every capability must declare its side effects in the `effects` field:
|
|
|
119
119
|
|
|
120
120
|
Effects are used by governance rules to analyze the system.
|
|
121
121
|
|
|
122
|
+
## Transactional outbox (default ON)
|
|
123
|
+
|
|
124
|
+
For `kind: 'action'` and `kind: 'eventHandler'`, handler execution and output validation run inside a single database transaction by default. Entity writes via `ctx.data.*` and `ctx.events.emit()` outbox inserts commit or roll back together.
|
|
125
|
+
|
|
126
|
+
**Auto-excluded** (non-transactional): `kind: 'job'`, `effects.ai: true`, non-empty `effects.external`, and other kinds (e.g. `query`).
|
|
127
|
+
|
|
128
|
+
**Recommended:** keep the default ON and fix handlers that assumed partial commits. There is no framework-wide legacy/soft mode.
|
|
129
|
+
|
|
130
|
+
**Opt out (per app or capability — not the long-term default):**
|
|
131
|
+
- Globally: `execution.transactionalOutbox: false` in config, or `PLUMBUS_TRANSACTIONAL_OUTBOX=false`
|
|
132
|
+
- Per capability: `transactional: false` on `defineCapability({ ... })`
|
|
133
|
+
|
|
134
|
+
Inside an active transaction, `ctx.flows.start()` and `ctx.jobs.enqueue()` defer until after commit (pre-allocated ids). Nested success audits also defer until commit. Prefer `transactional: false` on parents that invoke AI capabilities — otherwise the parent transaction is held open for the LLM call (one-time warn).
|
|
135
|
+
|
|
136
|
+
See `docs/upgrading-contract-alignment.md` (Migration stance + §1) and `docs/architecture/execution-lifecycle.md`.
|
|
137
|
+
|
|
122
138
|
## Capability-to-capability invocation
|
|
123
139
|
|
|
124
140
|
The only sanctioned path for synchronous composition is `ctx.capabilities.invoke`:
|
|
@@ -205,7 +221,9 @@ export const getRefund = defineCapability({
|
|
|
205
221
|
```
|
|
206
222
|
|
|
207
223
|
**Rules when MCP-exposed:**
|
|
208
|
-
-
|
|
224
|
+
- `kind: "query"` and `kind: "action"` are standard MCP tools.
|
|
225
|
+
- `kind: "job"` is exposed via MCP Tasks (`tools/call` + `_meta.taskMetadata`). Register `mcpTaskEntity` in the app entity list.
|
|
226
|
+
- Only `kind: "eventHandler"` is rejected at `defineCapability()` time for MCP exposure.
|
|
209
227
|
- Either `description`, `mcp.description`, or `explanation.summary` is required.
|
|
210
228
|
- Agent identity is controlled via `access.serviceAccounts` and `access.scopes`. Deny-by-default still holds.
|
|
211
229
|
|
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
|
|