@amboras-dev/capability-kit 0.0.0-stage → 0.2.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.
@@ -0,0 +1,314 @@
1
+ import { z } from "zod/v4";
2
+ import { MONEY_META_KEY, MONEY_UNIT_SENTENCE } from "@amboras-dev/money/ai";
3
+ //#region src/canonical-json.d.ts
4
+ /** Sorted keys, no whitespace. Throws on BigInt and circular values; `undefined` at the top serializes as `null`. */
5
+ export declare function canonicalJson(v: unknown): string;
6
+ //#endregion
7
+ //#region src/ids.d.ts
8
+ export declare const AREAS: readonly ["promotions", "catalog", "inventory", "setup", "content", "orders", "payments", "domains", "publish"];
9
+ type Area = (typeof AREAS)[number];
10
+ /** `<area>.<verb>` or `<area>.<entity>.<verb>`: lowercase segments of `a-z` and `_`. */
11
+ type CapabilityId = `${Area}.${string}`;
12
+ type Role = 'owner' | 'manager' | 'staff';
13
+ /** Higher rank includes every lower one: an owner can do what a manager can. */
14
+ export declare const ROLE_RANK: Readonly<Record<Role, number>>;
15
+ /** True for a well-formed id in a known area. */
16
+ export declare function isCapabilityId(value: unknown): value is CapabilityId;
17
+ /** The area an id belongs to (`promotions.create` → `promotions`). */
18
+ export declare function areaOf(id: CapabilityId): Area;
19
+ //#endregion
20
+ //#region src/context.d.ts
21
+ type Actor = {
22
+ kind: 'member';
23
+ profileId: string;
24
+ role: Role | null;
25
+ } | {
26
+ kind: 'internal';
27
+ service: string;
28
+ };
29
+ /** A label for logs. Never trusted for a policy decision. */
30
+ type Surface = 'ai' | 'admin' | 'api';
31
+ interface CapabilityCallContext {
32
+ tenantId: string;
33
+ actor: Actor;
34
+ surface: Surface;
35
+ runId?: string;
36
+ }
37
+ //#endregion
38
+ //#region src/json-schema.d.ts
39
+ type JsonSchema = {
40
+ [key: string]: unknown;
41
+ };
42
+ /**
43
+ * A copy of `schema` without the hidden fields (`agent.hideFields`), also
44
+ * dropped from `required`. Adapters build the model-facing input with this.
45
+ */
46
+ export declare function omitFields(schema: JsonSchema, fields: readonly string[]): JsonSchema;
47
+ //#endregion
48
+ //#region src/policy-types.d.ts
49
+ interface CapabilityPolicy {
50
+ minRole: Role;
51
+ planFeature?: string;
52
+ effect: 'read' | 'write' | 'destructive';
53
+ /** True = safe to retry without a key (reads, set-to-value). */
54
+ idempotent: boolean;
55
+ /** Grants or moves value: fail closed everywhere. */
56
+ money?: boolean;
57
+ confirm: 'never' | 'always' | {
58
+ when: {
59
+ recordsAtLeast: number;
60
+ };
61
+ };
62
+ rateLimit?: {
63
+ perMinute: number;
64
+ };
65
+ }
66
+ //#endregion
67
+ //#region src/define.d.ts
68
+ interface CapabilityAgentSpec {
69
+ /** What it does and when to use it, written for the model. */
70
+ description: string;
71
+ /** Only what the schema shape cannot carry. */
72
+ rules?: string[];
73
+ examples?: Array<{
74
+ input: unknown;
75
+ note: string;
76
+ }>;
77
+ /** Dotted input paths the model never sees; each must exist and be optional. */
78
+ hideFields?: string[];
79
+ exposure: 'task-tool' | 'long-tail' | 'hidden';
80
+ /** Phase 1: keeps the legacy tool name (e.g. 'create_promotion') so prompts, evals and TOOL_CAPABILITIES keys stay stable. */
81
+ toolName?: string;
82
+ }
83
+ interface CapabilityDefinition<I = unknown, O = unknown, Ctx = unknown> {
84
+ id: CapabilityId;
85
+ /** Integer ≥ 1; bump on a breaking input or output change. */
86
+ version: number;
87
+ owner: 'medusa' | 'orchestrator';
88
+ http: {
89
+ method: 'GET' | 'POST' | 'DELETE';
90
+ path: string;
91
+ runsOn: 'custom' | 'core';
92
+ };
93
+ input: z.ZodType<I>;
94
+ output: z.ZodType<O>;
95
+ run?(ctx: Ctx, input: I): Promise<O>;
96
+ policy: CapabilityPolicy;
97
+ agent: CapabilityAgentSpec;
98
+ }
99
+ /**
100
+ * Checks a definition and returns a frozen copy. Throws CapabilityDefinitionError
101
+ * on: a bad id or unknown area; a version that is not a positive integer; an
102
+ * input that is not an object, accepts undeclared keys or any value, or names a
103
+ * store / tenant / profile / environment id at any depth; runsOn 'custom'
104
+ * without run; hideFields naming a missing (or required) field; a schema JSON
105
+ * Schema cannot express; a money() field without its unit sentence or with a
106
+ * currencyField that is not a string field at the input root; an invalid
107
+ * minRole, confirm threshold, rate limit or toolName.
108
+ */
109
+ export declare function defineCapability<I, O, Ctx = unknown>(def: CapabilityDefinition<I, O, Ctx>): Readonly<CapabilityDefinition<I, O, Ctx>>;
110
+ //#endregion
111
+ //#region src/errors.d.ts
112
+ type CapabilityErrorCode = 'invalid_input' | 'forbidden' | 'not_found' | 'conflict' | 'confirmation_required' | 'rate_limited' | 'idempotency_conflict' | 'unavailable' | 'internal';
113
+ interface CapabilityErrorBody {
114
+ code: CapabilityErrorCode;
115
+ message: string;
116
+ /** Dotted input path the error is about (`discount.amount_off`). */
117
+ field?: string;
118
+ /** Stable id of the business rule that refused (`idempotency_key_required`). */
119
+ rule?: string;
120
+ retryable: boolean;
121
+ issues?: Array<{
122
+ field: string;
123
+ message: string;
124
+ }>;
125
+ /** Small structured facts the caller can act on (duplicate_code → { existing_id, existing_status }). Never secrets or PII. */
126
+ details?: Record<string, string | number | boolean | null>;
127
+ }
128
+ export declare function httpStatusFor(code: CapabilityErrorCode): number;
129
+ type CapabilityErrorInit = Omit<CapabilityErrorBody, 'retryable'> & {
130
+ retryable?: boolean;
131
+ };
132
+ export declare class CapabilityError extends Error {
133
+ readonly body: CapabilityErrorBody;
134
+ readonly status: number;
135
+ /** `retryable` defaults to true for `rate_limited` and `unavailable`, else false. */
136
+ constructor(init: CapabilityErrorInit);
137
+ }
138
+ /** A definition that breaks a kit rule. Thrown at boot, never per request. */
139
+ export declare class CapabilityDefinitionError extends Error {
140
+ constructor(message: string);
141
+ }
142
+ /** A manifest that fails its shape or hash check. */
143
+ export declare class CapabilityManifestError extends Error {
144
+ constructor(message: string);
145
+ }
146
+ interface FromZodErrorOptions {
147
+ /**
148
+ * Fields that no longer exist, keyed by the old name, valued with what to use
149
+ * instead. An unrecognized key listed here (strict objects only) answers
150
+ * `"value" was removed: use percent_off or amount_off (major units).` with
151
+ * rule `field_renamed`, instead of zod's generic message.
152
+ */
153
+ renamed?: Readonly<Record<string, string>>;
154
+ }
155
+ /**
156
+ * `invalid_input`: `field` is the dotted path of the first issue, `issues` lists
157
+ * all of them. Renamed-field issues (`options.renamed`) come first and make the headline. A custom issue's `params.rule` becomes `body.rule`; see
158
+ * `options.renamed` for steering removed field names.
159
+ */
160
+ export declare function fromZodError(err: z.ZodError, options?: FromZodErrorOptions): CapabilityError;
161
+ //#endregion
162
+ //#region src/idempotency.d.ts
163
+ /**
164
+ * 8–100 of `A-Z a-z 0-9 . _ : -`. The underscore is there because the
165
+ * orchestrator's key is `cap.{runId}.{toolCallId}` and model tool call ids
166
+ * contain one (`toolu_…`, `call_…`).
167
+ */
168
+ export declare const IDEMPOTENCY_KEY_PATTERN: RegExp;
169
+ /** A non-read capability that is not naturally idempotent needs an `Idempotency-Key`. */
170
+ export declare function requiresIdempotencyKey(policy: Pick<CapabilityPolicy, 'effect' | 'idempotent'>): boolean;
171
+ /** sha256 hex of canonicalJson({ id, input }): same key + different fingerprint = a different request. */
172
+ export declare function requestFingerprint(id: CapabilityId, input: unknown): string;
173
+ //#endregion
174
+ //#region src/manifest.d.ts
175
+ interface ManifestEntry {
176
+ id: CapabilityId;
177
+ version: number;
178
+ owner: 'medusa' | 'orchestrator';
179
+ http: CapabilityDefinition['http'];
180
+ input: JsonSchema;
181
+ output: JsonSchema;
182
+ policy: CapabilityPolicy;
183
+ agent: CapabilityAgentSpec;
184
+ }
185
+ interface CapabilityManifest {
186
+ schemaVersion: 1;
187
+ source: 'medusa' | 'orchestrator';
188
+ hash: string;
189
+ capabilities: ManifestEntry[];
190
+ }
191
+ /** Any definition, whatever its input, output and context types. */
192
+ type AnyCapabilityDefinition = Readonly<CapabilityDefinition<unknown, unknown, never>>;
193
+ /**
194
+ * Deterministic: entries sorted by id, schemas converted with
195
+ * `{ target: 'draft-2020-12', io }`, no timestamps. Every definition is checked
196
+ * again (the defineCapability rules). Throws on a duplicate id.
197
+ *
198
+ * The JSON Schema text depends on the zod version that emits it (zod 3.25 writes
199
+ * a discriminated union as anyOf, 4.3 as oneOf), so the hash is only comparable
200
+ * between emits on the SAME zod version. Consumers verify a received manifest
201
+ * with parseManifest; they never re-emit it to compare.
202
+ */
203
+ export declare function emitManifest(defs: readonly AnyCapabilityDefinition[], source: 'medusa' | 'orchestrator'): CapabilityManifest;
204
+ /**
205
+ * Validates the shape, re-runs the definition rules on every entry (tenant ids,
206
+ * open inputs, money sentence …) and recomputes the hash from the JSON as
207
+ * received, never from schemas. Throws CapabilityManifestError on any failure.
208
+ * The hash proves the file is intact, not who wrote it: trust comes from where
209
+ * the file is read (the committed copy), not from the hash.
210
+ */
211
+ export declare function parseManifest(json: unknown): CapabilityManifest;
212
+ //#endregion
213
+ //#region src/money.d.ts
214
+ /**
215
+ * A bad amount or currency code. It is an `invalid_input` CapabilityError, so an
216
+ * adapter that maps CapabilityError answers 400 with the rule (and the field,
217
+ * when the caller passed one) instead of a 500.
218
+ */
219
+ export declare class MoneyError extends CapabilityError {
220
+ constructor(message: string, rule: 'money_precision' | 'money_invalid' | 'currency_invalid', field?: string);
221
+ }
222
+ /**
223
+ * Where the amount's currency comes from. `currencyField` names a field resolved
224
+ * from the INPUT ROOT, at any depth of the money field (`discount.amount_off`
225
+ * with `{ currencyField: 'currency_code' }` reads the top-level `currency_code`).
226
+ */
227
+ type MoneyCurrency = {
228
+ currencyField: string;
229
+ } | {
230
+ currency: 'store';
231
+ };
232
+ type MoneyOptions = MoneyCurrency & {
233
+ /** What the amount is; prepended to the unit sentence. */
234
+ describe?: string;
235
+ /** Strict lower bound (`0` = must be more than zero). Default: amounts are ≥ 0. */
236
+ exclusiveMinimum?: number;
237
+ /** Inclusive upper bound. */
238
+ max?: number;
239
+ };
240
+ /**
241
+ * A major-unit amount: finite, ≥ 0 (or > `exclusiveMinimum`), at most `max`,
242
+ * described with MONEY_UNIT_SENTENCE and marked `x-amboras-money` in JSON Schema.
243
+ *
244
+ * Chained checks (`.positive()`, `.max()`, `.int()`) and `.optional()`,
245
+ * `.nullable()`, `.nullish()` are safe: the kit's JSON Schema keeps the sentence
246
+ * and the marker. `.describe()` replaces the sentence, which `defineCapability`
247
+ * rejects: pass `describe` here instead.
248
+ */
249
+ export declare function money(opts: MoneyOptions): z.ZodNumber;
250
+ /**
251
+ * Storage digits (`storageDigits` in @amboras-dev/money): 0 for JPY/KRW/VND…, 3 for
252
+ * KWD/BHD/OMR/TND/JOD/IQD/LYD, else 2. An unknown code throws `currency_invalid`.
253
+ */
254
+ export declare function fractionDigits(currency: string): 0 | 2 | 3;
255
+ /**
256
+ * Amboras storage rule (`storageScale` in @amboras-dev/money): ×1000 for the seven
257
+ * three-decimal codes, ×100 for every other code INCLUDING zero-decimal ones (¥5 is
258
+ * stored as 500). An unknown code throws `currency_invalid`.
259
+ */
260
+ export declare function storageScale(currency: string): 100 | 1000;
261
+ /**
262
+ * Major → stored minor integer: `fromMajor(major, currency).amount` from
263
+ * @amboras-dev/money. Throws MoneyError (`invalid_input`) when the amount has more
264
+ * fraction digits than the currency allows (19.999 USD, 5.5 JPY), is not a finite
265
+ * number, or does not fit (2^46 units of the currency's digits). Float noise is
266
+ * accepted (0.1 + 0.2 → 0.30); a real extra digit never is. Pass `field` to name the
267
+ * input field in the error.
268
+ */
269
+ export declare function toStorageAmount(major: number, currency: string, field?: string): number;
270
+ /**
271
+ * Stored minor amount → major units: `toMajorNumber(money(minor, currency))` from
272
+ * @amboras-dev/money. The stored amount must be a safe integer.
273
+ */
274
+ export declare function fromStorageAmount(minor: number, currency: string): number;
275
+ interface MoneyView {
276
+ amount: number;
277
+ currency: string;
278
+ display: string;
279
+ }
280
+ /** Output schema for a MoneyView (what `moneyView` returns), for capability outputs. */
281
+ export declare const moneyViewSchema: z.ZodType<MoneyView>;
282
+ /**
283
+ * A stored amount as the merchant reads it: `moneyView` from @amboras-dev/money/ai.
284
+ * `amount` is exact; `display` shows the currency's display digits, widened to keep a
285
+ * stored value finer than them (¥5.5 stays visible, HUF 435000 is `HUF 4,350`). An
286
+ * unknown code throws `currency_invalid`.
287
+ */
288
+ export declare function moneyView(minor: number, currency: string): MoneyView;
289
+ //#endregion
290
+ //#region src/policy.d.ts
291
+ /** Phase 1 adapters pass observe/observe. */
292
+ interface PolicyEnforcement {
293
+ role: 'enforce' | 'observe';
294
+ plan: 'enforce' | 'observe';
295
+ }
296
+ /** `observed` holds the rule ids an 'observe' check would have denied, for logging. */
297
+ type PolicyDecision = {
298
+ allow: true;
299
+ observed: string[];
300
+ } | {
301
+ allow: false;
302
+ error: CapabilityError;
303
+ };
304
+ export declare function evaluatePolicy(policy: CapabilityPolicy, ctx: CapabilityCallContext, facts: {
305
+ planFeatures?: string[];
306
+ }, enforcement: PolicyEnforcement): PolicyDecision;
307
+ /**
308
+ * True when the call needs an explicit confirmation before it runs. `records` is
309
+ * how many records it touches. A threshold policy with no count, or a count that
310
+ * is not a non-negative integer, requires confirmation (the adapter could not count).
311
+ */
312
+ export declare function confirmationRequired(policy: Pick<CapabilityPolicy, 'confirm'>, records?: number): boolean;
313
+ //#endregion
314
+ export { type Actor, type AnyCapabilityDefinition, type Area, type CapabilityAgentSpec, type CapabilityCallContext, type CapabilityDefinition, type CapabilityErrorBody, type CapabilityErrorCode, type CapabilityErrorInit, type CapabilityId, type CapabilityManifest, type CapabilityPolicy, type FromZodErrorOptions, type JsonSchema, MONEY_META_KEY, MONEY_UNIT_SENTENCE, type ManifestEntry, type MoneyCurrency, type MoneyOptions, type MoneyView, type PolicyDecision, type PolicyEnforcement, type Role, type Surface };
@@ -0,0 +1,314 @@
1
+ import { z } from "zod/v4";
2
+ import { MONEY_META_KEY, MONEY_UNIT_SENTENCE } from "@amboras-dev/money/ai";
3
+ //#region src/canonical-json.d.ts
4
+ /** Sorted keys, no whitespace. Throws on BigInt and circular values; `undefined` at the top serializes as `null`. */
5
+ export declare function canonicalJson(v: unknown): string;
6
+ //#endregion
7
+ //#region src/ids.d.ts
8
+ export declare const AREAS: readonly ["promotions", "catalog", "inventory", "setup", "content", "orders", "payments", "domains", "publish"];
9
+ type Area = (typeof AREAS)[number];
10
+ /** `<area>.<verb>` or `<area>.<entity>.<verb>`: lowercase segments of `a-z` and `_`. */
11
+ type CapabilityId = `${Area}.${string}`;
12
+ type Role = 'owner' | 'manager' | 'staff';
13
+ /** Higher rank includes every lower one: an owner can do what a manager can. */
14
+ export declare const ROLE_RANK: Readonly<Record<Role, number>>;
15
+ /** True for a well-formed id in a known area. */
16
+ export declare function isCapabilityId(value: unknown): value is CapabilityId;
17
+ /** The area an id belongs to (`promotions.create` → `promotions`). */
18
+ export declare function areaOf(id: CapabilityId): Area;
19
+ //#endregion
20
+ //#region src/context.d.ts
21
+ type Actor = {
22
+ kind: 'member';
23
+ profileId: string;
24
+ role: Role | null;
25
+ } | {
26
+ kind: 'internal';
27
+ service: string;
28
+ };
29
+ /** A label for logs. Never trusted for a policy decision. */
30
+ type Surface = 'ai' | 'admin' | 'api';
31
+ interface CapabilityCallContext {
32
+ tenantId: string;
33
+ actor: Actor;
34
+ surface: Surface;
35
+ runId?: string;
36
+ }
37
+ //#endregion
38
+ //#region src/json-schema.d.ts
39
+ type JsonSchema = {
40
+ [key: string]: unknown;
41
+ };
42
+ /**
43
+ * A copy of `schema` without the hidden fields (`agent.hideFields`), also
44
+ * dropped from `required`. Adapters build the model-facing input with this.
45
+ */
46
+ export declare function omitFields(schema: JsonSchema, fields: readonly string[]): JsonSchema;
47
+ //#endregion
48
+ //#region src/policy-types.d.ts
49
+ interface CapabilityPolicy {
50
+ minRole: Role;
51
+ planFeature?: string;
52
+ effect: 'read' | 'write' | 'destructive';
53
+ /** True = safe to retry without a key (reads, set-to-value). */
54
+ idempotent: boolean;
55
+ /** Grants or moves value: fail closed everywhere. */
56
+ money?: boolean;
57
+ confirm: 'never' | 'always' | {
58
+ when: {
59
+ recordsAtLeast: number;
60
+ };
61
+ };
62
+ rateLimit?: {
63
+ perMinute: number;
64
+ };
65
+ }
66
+ //#endregion
67
+ //#region src/define.d.ts
68
+ interface CapabilityAgentSpec {
69
+ /** What it does and when to use it, written for the model. */
70
+ description: string;
71
+ /** Only what the schema shape cannot carry. */
72
+ rules?: string[];
73
+ examples?: Array<{
74
+ input: unknown;
75
+ note: string;
76
+ }>;
77
+ /** Dotted input paths the model never sees; each must exist and be optional. */
78
+ hideFields?: string[];
79
+ exposure: 'task-tool' | 'long-tail' | 'hidden';
80
+ /** Phase 1: keeps the legacy tool name (e.g. 'create_promotion') so prompts, evals and TOOL_CAPABILITIES keys stay stable. */
81
+ toolName?: string;
82
+ }
83
+ interface CapabilityDefinition<I = unknown, O = unknown, Ctx = unknown> {
84
+ id: CapabilityId;
85
+ /** Integer ≥ 1; bump on a breaking input or output change. */
86
+ version: number;
87
+ owner: 'medusa' | 'orchestrator';
88
+ http: {
89
+ method: 'GET' | 'POST' | 'DELETE';
90
+ path: string;
91
+ runsOn: 'custom' | 'core';
92
+ };
93
+ input: z.ZodType<I>;
94
+ output: z.ZodType<O>;
95
+ run?(ctx: Ctx, input: I): Promise<O>;
96
+ policy: CapabilityPolicy;
97
+ agent: CapabilityAgentSpec;
98
+ }
99
+ /**
100
+ * Checks a definition and returns a frozen copy. Throws CapabilityDefinitionError
101
+ * on: a bad id or unknown area; a version that is not a positive integer; an
102
+ * input that is not an object, accepts undeclared keys or any value, or names a
103
+ * store / tenant / profile / environment id at any depth; runsOn 'custom'
104
+ * without run; hideFields naming a missing (or required) field; a schema JSON
105
+ * Schema cannot express; a money() field without its unit sentence or with a
106
+ * currencyField that is not a string field at the input root; an invalid
107
+ * minRole, confirm threshold, rate limit or toolName.
108
+ */
109
+ export declare function defineCapability<I, O, Ctx = unknown>(def: CapabilityDefinition<I, O, Ctx>): Readonly<CapabilityDefinition<I, O, Ctx>>;
110
+ //#endregion
111
+ //#region src/errors.d.ts
112
+ type CapabilityErrorCode = 'invalid_input' | 'forbidden' | 'not_found' | 'conflict' | 'confirmation_required' | 'rate_limited' | 'idempotency_conflict' | 'unavailable' | 'internal';
113
+ interface CapabilityErrorBody {
114
+ code: CapabilityErrorCode;
115
+ message: string;
116
+ /** Dotted input path the error is about (`discount.amount_off`). */
117
+ field?: string;
118
+ /** Stable id of the business rule that refused (`idempotency_key_required`). */
119
+ rule?: string;
120
+ retryable: boolean;
121
+ issues?: Array<{
122
+ field: string;
123
+ message: string;
124
+ }>;
125
+ /** Small structured facts the caller can act on (duplicate_code → { existing_id, existing_status }). Never secrets or PII. */
126
+ details?: Record<string, string | number | boolean | null>;
127
+ }
128
+ export declare function httpStatusFor(code: CapabilityErrorCode): number;
129
+ type CapabilityErrorInit = Omit<CapabilityErrorBody, 'retryable'> & {
130
+ retryable?: boolean;
131
+ };
132
+ export declare class CapabilityError extends Error {
133
+ readonly body: CapabilityErrorBody;
134
+ readonly status: number;
135
+ /** `retryable` defaults to true for `rate_limited` and `unavailable`, else false. */
136
+ constructor(init: CapabilityErrorInit);
137
+ }
138
+ /** A definition that breaks a kit rule. Thrown at boot, never per request. */
139
+ export declare class CapabilityDefinitionError extends Error {
140
+ constructor(message: string);
141
+ }
142
+ /** A manifest that fails its shape or hash check. */
143
+ export declare class CapabilityManifestError extends Error {
144
+ constructor(message: string);
145
+ }
146
+ interface FromZodErrorOptions {
147
+ /**
148
+ * Fields that no longer exist, keyed by the old name, valued with what to use
149
+ * instead. An unrecognized key listed here (strict objects only) answers
150
+ * `"value" was removed: use percent_off or amount_off (major units).` with
151
+ * rule `field_renamed`, instead of zod's generic message.
152
+ */
153
+ renamed?: Readonly<Record<string, string>>;
154
+ }
155
+ /**
156
+ * `invalid_input`: `field` is the dotted path of the first issue, `issues` lists
157
+ * all of them. Renamed-field issues (`options.renamed`) come first and make the headline. A custom issue's `params.rule` becomes `body.rule`; see
158
+ * `options.renamed` for steering removed field names.
159
+ */
160
+ export declare function fromZodError(err: z.ZodError, options?: FromZodErrorOptions): CapabilityError;
161
+ //#endregion
162
+ //#region src/idempotency.d.ts
163
+ /**
164
+ * 8–100 of `A-Z a-z 0-9 . _ : -`. The underscore is there because the
165
+ * orchestrator's key is `cap.{runId}.{toolCallId}` and model tool call ids
166
+ * contain one (`toolu_…`, `call_…`).
167
+ */
168
+ export declare const IDEMPOTENCY_KEY_PATTERN: RegExp;
169
+ /** A non-read capability that is not naturally idempotent needs an `Idempotency-Key`. */
170
+ export declare function requiresIdempotencyKey(policy: Pick<CapabilityPolicy, 'effect' | 'idempotent'>): boolean;
171
+ /** sha256 hex of canonicalJson({ id, input }): same key + different fingerprint = a different request. */
172
+ export declare function requestFingerprint(id: CapabilityId, input: unknown): string;
173
+ //#endregion
174
+ //#region src/manifest.d.ts
175
+ interface ManifestEntry {
176
+ id: CapabilityId;
177
+ version: number;
178
+ owner: 'medusa' | 'orchestrator';
179
+ http: CapabilityDefinition['http'];
180
+ input: JsonSchema;
181
+ output: JsonSchema;
182
+ policy: CapabilityPolicy;
183
+ agent: CapabilityAgentSpec;
184
+ }
185
+ interface CapabilityManifest {
186
+ schemaVersion: 1;
187
+ source: 'medusa' | 'orchestrator';
188
+ hash: string;
189
+ capabilities: ManifestEntry[];
190
+ }
191
+ /** Any definition, whatever its input, output and context types. */
192
+ type AnyCapabilityDefinition = Readonly<CapabilityDefinition<unknown, unknown, never>>;
193
+ /**
194
+ * Deterministic: entries sorted by id, schemas converted with
195
+ * `{ target: 'draft-2020-12', io }`, no timestamps. Every definition is checked
196
+ * again (the defineCapability rules). Throws on a duplicate id.
197
+ *
198
+ * The JSON Schema text depends on the zod version that emits it (zod 3.25 writes
199
+ * a discriminated union as anyOf, 4.3 as oneOf), so the hash is only comparable
200
+ * between emits on the SAME zod version. Consumers verify a received manifest
201
+ * with parseManifest; they never re-emit it to compare.
202
+ */
203
+ export declare function emitManifest(defs: readonly AnyCapabilityDefinition[], source: 'medusa' | 'orchestrator'): CapabilityManifest;
204
+ /**
205
+ * Validates the shape, re-runs the definition rules on every entry (tenant ids,
206
+ * open inputs, money sentence …) and recomputes the hash from the JSON as
207
+ * received, never from schemas. Throws CapabilityManifestError on any failure.
208
+ * The hash proves the file is intact, not who wrote it: trust comes from where
209
+ * the file is read (the committed copy), not from the hash.
210
+ */
211
+ export declare function parseManifest(json: unknown): CapabilityManifest;
212
+ //#endregion
213
+ //#region src/money.d.ts
214
+ /**
215
+ * A bad amount or currency code. It is an `invalid_input` CapabilityError, so an
216
+ * adapter that maps CapabilityError answers 400 with the rule (and the field,
217
+ * when the caller passed one) instead of a 500.
218
+ */
219
+ export declare class MoneyError extends CapabilityError {
220
+ constructor(message: string, rule: 'money_precision' | 'money_invalid' | 'currency_invalid', field?: string);
221
+ }
222
+ /**
223
+ * Where the amount's currency comes from. `currencyField` names a field resolved
224
+ * from the INPUT ROOT, at any depth of the money field (`discount.amount_off`
225
+ * with `{ currencyField: 'currency_code' }` reads the top-level `currency_code`).
226
+ */
227
+ type MoneyCurrency = {
228
+ currencyField: string;
229
+ } | {
230
+ currency: 'store';
231
+ };
232
+ type MoneyOptions = MoneyCurrency & {
233
+ /** What the amount is; prepended to the unit sentence. */
234
+ describe?: string;
235
+ /** Strict lower bound (`0` = must be more than zero). Default: amounts are ≥ 0. */
236
+ exclusiveMinimum?: number;
237
+ /** Inclusive upper bound. */
238
+ max?: number;
239
+ };
240
+ /**
241
+ * A major-unit amount: finite, ≥ 0 (or > `exclusiveMinimum`), at most `max`,
242
+ * described with MONEY_UNIT_SENTENCE and marked `x-amboras-money` in JSON Schema.
243
+ *
244
+ * Chained checks (`.positive()`, `.max()`, `.int()`) and `.optional()`,
245
+ * `.nullable()`, `.nullish()` are safe: the kit's JSON Schema keeps the sentence
246
+ * and the marker. `.describe()` replaces the sentence, which `defineCapability`
247
+ * rejects: pass `describe` here instead.
248
+ */
249
+ export declare function money(opts: MoneyOptions): z.ZodNumber;
250
+ /**
251
+ * Storage digits (`storageDigits` in @amboras-dev/money): 0 for JPY/KRW/VND…, 3 for
252
+ * KWD/BHD/OMR/TND/JOD/IQD/LYD, else 2. An unknown code throws `currency_invalid`.
253
+ */
254
+ export declare function fractionDigits(currency: string): 0 | 2 | 3;
255
+ /**
256
+ * Amboras storage rule (`storageScale` in @amboras-dev/money): ×1000 for the seven
257
+ * three-decimal codes, ×100 for every other code INCLUDING zero-decimal ones (¥5 is
258
+ * stored as 500). An unknown code throws `currency_invalid`.
259
+ */
260
+ export declare function storageScale(currency: string): 100 | 1000;
261
+ /**
262
+ * Major → stored minor integer: `fromMajor(major, currency).amount` from
263
+ * @amboras-dev/money. Throws MoneyError (`invalid_input`) when the amount has more
264
+ * fraction digits than the currency allows (19.999 USD, 5.5 JPY), is not a finite
265
+ * number, or does not fit (2^46 units of the currency's digits). Float noise is
266
+ * accepted (0.1 + 0.2 → 0.30); a real extra digit never is. Pass `field` to name the
267
+ * input field in the error.
268
+ */
269
+ export declare function toStorageAmount(major: number, currency: string, field?: string): number;
270
+ /**
271
+ * Stored minor amount → major units: `toMajorNumber(money(minor, currency))` from
272
+ * @amboras-dev/money. The stored amount must be a safe integer.
273
+ */
274
+ export declare function fromStorageAmount(minor: number, currency: string): number;
275
+ interface MoneyView {
276
+ amount: number;
277
+ currency: string;
278
+ display: string;
279
+ }
280
+ /** Output schema for a MoneyView (what `moneyView` returns), for capability outputs. */
281
+ export declare const moneyViewSchema: z.ZodType<MoneyView>;
282
+ /**
283
+ * A stored amount as the merchant reads it: `moneyView` from @amboras-dev/money/ai.
284
+ * `amount` is exact; `display` shows the currency's display digits, widened to keep a
285
+ * stored value finer than them (¥5.5 stays visible, HUF 435000 is `HUF 4,350`). An
286
+ * unknown code throws `currency_invalid`.
287
+ */
288
+ export declare function moneyView(minor: number, currency: string): MoneyView;
289
+ //#endregion
290
+ //#region src/policy.d.ts
291
+ /** Phase 1 adapters pass observe/observe. */
292
+ interface PolicyEnforcement {
293
+ role: 'enforce' | 'observe';
294
+ plan: 'enforce' | 'observe';
295
+ }
296
+ /** `observed` holds the rule ids an 'observe' check would have denied, for logging. */
297
+ type PolicyDecision = {
298
+ allow: true;
299
+ observed: string[];
300
+ } | {
301
+ allow: false;
302
+ error: CapabilityError;
303
+ };
304
+ export declare function evaluatePolicy(policy: CapabilityPolicy, ctx: CapabilityCallContext, facts: {
305
+ planFeatures?: string[];
306
+ }, enforcement: PolicyEnforcement): PolicyDecision;
307
+ /**
308
+ * True when the call needs an explicit confirmation before it runs. `records` is
309
+ * how many records it touches. A threshold policy with no count, or a count that
310
+ * is not a non-negative integer, requires confirmation (the adapter could not count).
311
+ */
312
+ export declare function confirmationRequired(policy: Pick<CapabilityPolicy, 'confirm'>, records?: number): boolean;
313
+ //#endregion
314
+ export { type Actor, type AnyCapabilityDefinition, type Area, type CapabilityAgentSpec, type CapabilityCallContext, type CapabilityDefinition, type CapabilityErrorBody, type CapabilityErrorCode, type CapabilityErrorInit, type CapabilityId, type CapabilityManifest, type CapabilityPolicy, type FromZodErrorOptions, type JsonSchema, MONEY_META_KEY, MONEY_UNIT_SENTENCE, type ManifestEntry, type MoneyCurrency, type MoneyOptions, type MoneyView, type PolicyDecision, type PolicyEnforcement, type Role, type Surface };