@permission-protocol/sdk 0.1.0-alpha.1

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,297 @@
1
+ /**
2
+ * HashablePayload v1 Implementation
3
+ * Conforms to spec/hashable-payload-v1.md
4
+ */
5
+ /**
6
+ * HashablePayload v1 - the canonical envelope for computing inputHash.
7
+ * Matches spec/hashable-payload-v1.md exactly.
8
+ */
9
+ interface HashablePayloadV1 {
10
+ tool: string;
11
+ operation: string;
12
+ input: unknown;
13
+ metadata: Record<string, unknown> | null;
14
+ }
15
+ /**
16
+ * Canonicalize any JSON value.
17
+ *
18
+ * Rules (from spec/hashable-payload-v1.md):
19
+ * - null → "null"
20
+ * - primitives → JSON.stringify(value)
21
+ * - arrays → "[" + items.map(canonicalize).join(",") + "]"
22
+ * - objects: sort keys lexicographically, omit undefined values
23
+ */
24
+ declare function canonicalize(obj: unknown): string;
25
+ /**
26
+ * Compute inputHash from HashablePayload v1.
27
+ *
28
+ * Algorithm (from spec/hashable-payload-v1.md):
29
+ * 1. Canonicalize the payload
30
+ * 2. SHA-256 hash (UTF-8 encoded)
31
+ * 3. Lowercase hex encoding
32
+ * 4. Prefix with "sha256:"
33
+ */
34
+ declare function computeHash(payload: HashablePayloadV1): string;
35
+ /**
36
+ * Build HashablePayload v1 from SDK execute options.
37
+ */
38
+ declare function buildHashablePayload(opts: {
39
+ tool: string;
40
+ operation: string;
41
+ input: unknown;
42
+ metadata?: Record<string, unknown>;
43
+ }): HashablePayloadV1;
44
+
45
+ /**
46
+ * Permission Protocol SDK Types
47
+ *
48
+ * Public types for the SDK. Internal types are not exported.
49
+ */
50
+
51
+ interface ConfigureOptions {
52
+ /**
53
+ * The Permission Protocol API endpoint.
54
+ * Example: 'https://api.permissionprotocol.com'
55
+ */
56
+ endpoint: string;
57
+ /**
58
+ * API key for authentication.
59
+ */
60
+ apiKey: string;
61
+ /**
62
+ * Request timeout in milliseconds.
63
+ * Default: 10000 (10 seconds)
64
+ */
65
+ timeoutMs?: number;
66
+ /**
67
+ * Protocol version to use.
68
+ * Default: 1
69
+ */
70
+ protocolVersion?: number;
71
+ /**
72
+ * SDK version string (sent in X-PP-SDK-Version header).
73
+ * Default: package.json version
74
+ */
75
+ sdkVersion?: string;
76
+ /**
77
+ * Optional receipt verification hook.
78
+ * Called after receiving a receipt from hosted PP.
79
+ * Return false to reject the receipt (throws INVALID_RECEIPT).
80
+ * Default: no-op (always returns true)
81
+ *
82
+ * Use this hook for future signature verification.
83
+ */
84
+ verifyReceipt?: (receipt: PermissionReceipt) => boolean | Promise<boolean>;
85
+ }
86
+ interface ExecuteOptions<TInput> {
87
+ /**
88
+ * Tenant/organization identifier.
89
+ */
90
+ tenantId: string;
91
+ /**
92
+ * Agent identifier making the request.
93
+ */
94
+ agentId: string;
95
+ /**
96
+ * Tool being invoked (e.g., 'wordpress', 'github', 'slack').
97
+ */
98
+ tool: string;
99
+ /**
100
+ * Operation on the tool (e.g., 'publish_post', 'create_pr').
101
+ */
102
+ operation: string;
103
+ /**
104
+ * Input payload for the operation.
105
+ * Can be any JSON-serializable value.
106
+ */
107
+ input: TInput;
108
+ /**
109
+ * Optional metadata for risk assessment.
110
+ * Included in the input hash.
111
+ */
112
+ metadata?: Record<string, unknown>;
113
+ /**
114
+ * Optional reversibility hint for risk assessment.
115
+ * Default: 'UNKNOWN' (no policy signal encoded by SDK)
116
+ */
117
+ reversibility?: 'REVERSIBLE' | 'PARTIALLY_REVERSIBLE' | 'IRREVERSIBLE' | 'UNKNOWN';
118
+ /**
119
+ * Execution mode.
120
+ * - 'hosted': Connect to hosted Permission Protocol (default)
121
+ * - 'dev': Local development mode with auto-approval
122
+ */
123
+ mode?: 'hosted' | 'dev';
124
+ }
125
+ type ExecuteResult<TResult> = {
126
+ status: 'APPROVED';
127
+ receipt: PermissionReceipt;
128
+ result?: TResult;
129
+ } | {
130
+ status: 'REQUIRES_APPROVAL';
131
+ receipt: PermissionReceipt;
132
+ } | {
133
+ status: 'DENIED';
134
+ receipt: PermissionReceipt;
135
+ };
136
+ interface PermissionReceipt {
137
+ /**
138
+ * Unique receipt identifier.
139
+ */
140
+ receiptId: string;
141
+ /**
142
+ * Tenant/organization identifier.
143
+ */
144
+ tenantId: string;
145
+ /**
146
+ * Agent that requested authorization.
147
+ */
148
+ agentId: string;
149
+ /**
150
+ * Tool that was requested.
151
+ */
152
+ tool: string;
153
+ /**
154
+ * Operation that was requested.
155
+ */
156
+ operation: string;
157
+ /**
158
+ * SHA-256 hash of the canonical input payload.
159
+ * Format: 'sha256:<hex>'
160
+ */
161
+ inputHash: string;
162
+ /**
163
+ * The decision from Permission Protocol.
164
+ */
165
+ decision: 'APPROVED' | 'DENIED' | 'REQUIRES_APPROVAL';
166
+ /**
167
+ * Reason codes explaining the decision.
168
+ * May include: REQUIRES_FOUNDER_VETO, APPROVAL_EXPIRED, EXECUTION_ERROR, etc.
169
+ */
170
+ reasonCodes: string[];
171
+ /**
172
+ * Who approved the action.
173
+ * - 'policy': Auto-approved by policy
174
+ * - 'human': Approved by a human
175
+ * - 'founder': Approved by founder
176
+ * - 'dev_mock': Dev mode auto-approval (NOT valid for production)
177
+ */
178
+ approver: 'policy' | 'human' | 'founder' | 'dev_mock';
179
+ /**
180
+ * Policy version that made the decision (if available).
181
+ */
182
+ policyVersion?: string;
183
+ /**
184
+ * When the receipt was created (ISO 8601).
185
+ */
186
+ createdAt: string;
187
+ /**
188
+ * Receipt signature (future-proof placeholder).
189
+ * Used for verifying receipt authenticity.
190
+ */
191
+ receiptSig?: string;
192
+ /**
193
+ * Signature algorithm used (future-proof placeholder).
194
+ */
195
+ sigAlg?: 'ed25519' | 'hmac-sha256';
196
+ }
197
+
198
+ /**
199
+ * PermissionRouter - Main SDK Entry Point
200
+ *
201
+ * The only two methods developers need:
202
+ * - PermissionRouter.configure(opts) - Configure the client
203
+ * - PermissionRouter.execute(opts) - Request authorization
204
+ *
205
+ * This SDK does NOT:
206
+ * - Make decisions
207
+ * - Execute tools
208
+ * - Store receipts
209
+ * - Implement policy logic
210
+ */
211
+
212
+ /**
213
+ * Permission Protocol SDK
214
+ *
215
+ * Standardized authorization for AI agents.
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * import { PermissionRouter } from '@permission-protocol/sdk';
220
+ *
221
+ * // Configure once
222
+ * PermissionRouter.configure({
223
+ * endpoint: 'https://api.permissionprotocol.com',
224
+ * apiKey: process.env.PP_API_KEY!,
225
+ * });
226
+ *
227
+ * // Request authorization
228
+ * const decision = await PermissionRouter.execute({
229
+ * tenantId: 'acme',
230
+ * agentId: 'seo-agent',
231
+ * tool: 'wordpress',
232
+ * operation: 'publish_post',
233
+ * input: postPayload,
234
+ * });
235
+ *
236
+ * if (decision.status !== 'APPROVED') {
237
+ * // Handle denial or pending approval
238
+ * process.exit(2);
239
+ * }
240
+ *
241
+ * // Tool execution happens elsewhere - SDK does NOT execute tools
242
+ * ```
243
+ */
244
+ declare const PermissionRouter: {
245
+ /**
246
+ * Configure the Permission Protocol client.
247
+ *
248
+ * Must be called before execute() in hosted mode.
249
+ *
250
+ * @param opts - Configuration options
251
+ */
252
+ configure(opts: ConfigureOptions): void;
253
+ /**
254
+ * Request authorization for an action.
255
+ *
256
+ * Returns a decision + receipt. Does NOT execute the action.
257
+ *
258
+ * @param opts - Execute options
259
+ * @returns Promise resolving to decision (APPROVED, REQUIRES_APPROVAL, or DENIED) with receipt
260
+ * @throws PermissionProtocolError on network failure, timeout, or invalid response
261
+ */
262
+ execute<TInput, TResult = unknown>(opts: ExecuteOptions<TInput>): Promise<ExecuteResult<TResult>>;
263
+ /**
264
+ * Check if the client is configured.
265
+ * Useful for testing and debugging.
266
+ */
267
+ isConfigured(): boolean;
268
+ /**
269
+ * Reset configuration (for testing).
270
+ * @internal
271
+ */
272
+ _reset(): void;
273
+ };
274
+
275
+ /**
276
+ * Permission Protocol SDK Errors
277
+ *
278
+ * Fail-closed error handling. No silent success.
279
+ */
280
+ /**
281
+ * Error codes for Permission Protocol errors.
282
+ */
283
+ type ErrorCode = 'EXECUTION_UNAUTHORIZED' | 'INVALID_RECEIPT' | 'MISCONFIGURED';
284
+ /**
285
+ * Error thrown by Permission Protocol SDK.
286
+ *
287
+ * The SDK fails closed on all errors - there is no silent success path.
288
+ */
289
+ declare class PermissionProtocolError extends Error {
290
+ /**
291
+ * Error code for programmatic handling.
292
+ */
293
+ readonly code: ErrorCode;
294
+ constructor(code: ErrorCode, message: string);
295
+ }
296
+
297
+ export { type ConfigureOptions, type ErrorCode, type ExecuteOptions, type ExecuteResult, type HashablePayloadV1, PermissionProtocolError, type PermissionReceipt, PermissionRouter, buildHashablePayload, canonicalize, computeHash };
@@ -0,0 +1,297 @@
1
+ /**
2
+ * HashablePayload v1 Implementation
3
+ * Conforms to spec/hashable-payload-v1.md
4
+ */
5
+ /**
6
+ * HashablePayload v1 - the canonical envelope for computing inputHash.
7
+ * Matches spec/hashable-payload-v1.md exactly.
8
+ */
9
+ interface HashablePayloadV1 {
10
+ tool: string;
11
+ operation: string;
12
+ input: unknown;
13
+ metadata: Record<string, unknown> | null;
14
+ }
15
+ /**
16
+ * Canonicalize any JSON value.
17
+ *
18
+ * Rules (from spec/hashable-payload-v1.md):
19
+ * - null → "null"
20
+ * - primitives → JSON.stringify(value)
21
+ * - arrays → "[" + items.map(canonicalize).join(",") + "]"
22
+ * - objects: sort keys lexicographically, omit undefined values
23
+ */
24
+ declare function canonicalize(obj: unknown): string;
25
+ /**
26
+ * Compute inputHash from HashablePayload v1.
27
+ *
28
+ * Algorithm (from spec/hashable-payload-v1.md):
29
+ * 1. Canonicalize the payload
30
+ * 2. SHA-256 hash (UTF-8 encoded)
31
+ * 3. Lowercase hex encoding
32
+ * 4. Prefix with "sha256:"
33
+ */
34
+ declare function computeHash(payload: HashablePayloadV1): string;
35
+ /**
36
+ * Build HashablePayload v1 from SDK execute options.
37
+ */
38
+ declare function buildHashablePayload(opts: {
39
+ tool: string;
40
+ operation: string;
41
+ input: unknown;
42
+ metadata?: Record<string, unknown>;
43
+ }): HashablePayloadV1;
44
+
45
+ /**
46
+ * Permission Protocol SDK Types
47
+ *
48
+ * Public types for the SDK. Internal types are not exported.
49
+ */
50
+
51
+ interface ConfigureOptions {
52
+ /**
53
+ * The Permission Protocol API endpoint.
54
+ * Example: 'https://api.permissionprotocol.com'
55
+ */
56
+ endpoint: string;
57
+ /**
58
+ * API key for authentication.
59
+ */
60
+ apiKey: string;
61
+ /**
62
+ * Request timeout in milliseconds.
63
+ * Default: 10000 (10 seconds)
64
+ */
65
+ timeoutMs?: number;
66
+ /**
67
+ * Protocol version to use.
68
+ * Default: 1
69
+ */
70
+ protocolVersion?: number;
71
+ /**
72
+ * SDK version string (sent in X-PP-SDK-Version header).
73
+ * Default: package.json version
74
+ */
75
+ sdkVersion?: string;
76
+ /**
77
+ * Optional receipt verification hook.
78
+ * Called after receiving a receipt from hosted PP.
79
+ * Return false to reject the receipt (throws INVALID_RECEIPT).
80
+ * Default: no-op (always returns true)
81
+ *
82
+ * Use this hook for future signature verification.
83
+ */
84
+ verifyReceipt?: (receipt: PermissionReceipt) => boolean | Promise<boolean>;
85
+ }
86
+ interface ExecuteOptions<TInput> {
87
+ /**
88
+ * Tenant/organization identifier.
89
+ */
90
+ tenantId: string;
91
+ /**
92
+ * Agent identifier making the request.
93
+ */
94
+ agentId: string;
95
+ /**
96
+ * Tool being invoked (e.g., 'wordpress', 'github', 'slack').
97
+ */
98
+ tool: string;
99
+ /**
100
+ * Operation on the tool (e.g., 'publish_post', 'create_pr').
101
+ */
102
+ operation: string;
103
+ /**
104
+ * Input payload for the operation.
105
+ * Can be any JSON-serializable value.
106
+ */
107
+ input: TInput;
108
+ /**
109
+ * Optional metadata for risk assessment.
110
+ * Included in the input hash.
111
+ */
112
+ metadata?: Record<string, unknown>;
113
+ /**
114
+ * Optional reversibility hint for risk assessment.
115
+ * Default: 'UNKNOWN' (no policy signal encoded by SDK)
116
+ */
117
+ reversibility?: 'REVERSIBLE' | 'PARTIALLY_REVERSIBLE' | 'IRREVERSIBLE' | 'UNKNOWN';
118
+ /**
119
+ * Execution mode.
120
+ * - 'hosted': Connect to hosted Permission Protocol (default)
121
+ * - 'dev': Local development mode with auto-approval
122
+ */
123
+ mode?: 'hosted' | 'dev';
124
+ }
125
+ type ExecuteResult<TResult> = {
126
+ status: 'APPROVED';
127
+ receipt: PermissionReceipt;
128
+ result?: TResult;
129
+ } | {
130
+ status: 'REQUIRES_APPROVAL';
131
+ receipt: PermissionReceipt;
132
+ } | {
133
+ status: 'DENIED';
134
+ receipt: PermissionReceipt;
135
+ };
136
+ interface PermissionReceipt {
137
+ /**
138
+ * Unique receipt identifier.
139
+ */
140
+ receiptId: string;
141
+ /**
142
+ * Tenant/organization identifier.
143
+ */
144
+ tenantId: string;
145
+ /**
146
+ * Agent that requested authorization.
147
+ */
148
+ agentId: string;
149
+ /**
150
+ * Tool that was requested.
151
+ */
152
+ tool: string;
153
+ /**
154
+ * Operation that was requested.
155
+ */
156
+ operation: string;
157
+ /**
158
+ * SHA-256 hash of the canonical input payload.
159
+ * Format: 'sha256:<hex>'
160
+ */
161
+ inputHash: string;
162
+ /**
163
+ * The decision from Permission Protocol.
164
+ */
165
+ decision: 'APPROVED' | 'DENIED' | 'REQUIRES_APPROVAL';
166
+ /**
167
+ * Reason codes explaining the decision.
168
+ * May include: REQUIRES_FOUNDER_VETO, APPROVAL_EXPIRED, EXECUTION_ERROR, etc.
169
+ */
170
+ reasonCodes: string[];
171
+ /**
172
+ * Who approved the action.
173
+ * - 'policy': Auto-approved by policy
174
+ * - 'human': Approved by a human
175
+ * - 'founder': Approved by founder
176
+ * - 'dev_mock': Dev mode auto-approval (NOT valid for production)
177
+ */
178
+ approver: 'policy' | 'human' | 'founder' | 'dev_mock';
179
+ /**
180
+ * Policy version that made the decision (if available).
181
+ */
182
+ policyVersion?: string;
183
+ /**
184
+ * When the receipt was created (ISO 8601).
185
+ */
186
+ createdAt: string;
187
+ /**
188
+ * Receipt signature (future-proof placeholder).
189
+ * Used for verifying receipt authenticity.
190
+ */
191
+ receiptSig?: string;
192
+ /**
193
+ * Signature algorithm used (future-proof placeholder).
194
+ */
195
+ sigAlg?: 'ed25519' | 'hmac-sha256';
196
+ }
197
+
198
+ /**
199
+ * PermissionRouter - Main SDK Entry Point
200
+ *
201
+ * The only two methods developers need:
202
+ * - PermissionRouter.configure(opts) - Configure the client
203
+ * - PermissionRouter.execute(opts) - Request authorization
204
+ *
205
+ * This SDK does NOT:
206
+ * - Make decisions
207
+ * - Execute tools
208
+ * - Store receipts
209
+ * - Implement policy logic
210
+ */
211
+
212
+ /**
213
+ * Permission Protocol SDK
214
+ *
215
+ * Standardized authorization for AI agents.
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * import { PermissionRouter } from '@permission-protocol/sdk';
220
+ *
221
+ * // Configure once
222
+ * PermissionRouter.configure({
223
+ * endpoint: 'https://api.permissionprotocol.com',
224
+ * apiKey: process.env.PP_API_KEY!,
225
+ * });
226
+ *
227
+ * // Request authorization
228
+ * const decision = await PermissionRouter.execute({
229
+ * tenantId: 'acme',
230
+ * agentId: 'seo-agent',
231
+ * tool: 'wordpress',
232
+ * operation: 'publish_post',
233
+ * input: postPayload,
234
+ * });
235
+ *
236
+ * if (decision.status !== 'APPROVED') {
237
+ * // Handle denial or pending approval
238
+ * process.exit(2);
239
+ * }
240
+ *
241
+ * // Tool execution happens elsewhere - SDK does NOT execute tools
242
+ * ```
243
+ */
244
+ declare const PermissionRouter: {
245
+ /**
246
+ * Configure the Permission Protocol client.
247
+ *
248
+ * Must be called before execute() in hosted mode.
249
+ *
250
+ * @param opts - Configuration options
251
+ */
252
+ configure(opts: ConfigureOptions): void;
253
+ /**
254
+ * Request authorization for an action.
255
+ *
256
+ * Returns a decision + receipt. Does NOT execute the action.
257
+ *
258
+ * @param opts - Execute options
259
+ * @returns Promise resolving to decision (APPROVED, REQUIRES_APPROVAL, or DENIED) with receipt
260
+ * @throws PermissionProtocolError on network failure, timeout, or invalid response
261
+ */
262
+ execute<TInput, TResult = unknown>(opts: ExecuteOptions<TInput>): Promise<ExecuteResult<TResult>>;
263
+ /**
264
+ * Check if the client is configured.
265
+ * Useful for testing and debugging.
266
+ */
267
+ isConfigured(): boolean;
268
+ /**
269
+ * Reset configuration (for testing).
270
+ * @internal
271
+ */
272
+ _reset(): void;
273
+ };
274
+
275
+ /**
276
+ * Permission Protocol SDK Errors
277
+ *
278
+ * Fail-closed error handling. No silent success.
279
+ */
280
+ /**
281
+ * Error codes for Permission Protocol errors.
282
+ */
283
+ type ErrorCode = 'EXECUTION_UNAUTHORIZED' | 'INVALID_RECEIPT' | 'MISCONFIGURED';
284
+ /**
285
+ * Error thrown by Permission Protocol SDK.
286
+ *
287
+ * The SDK fails closed on all errors - there is no silent success path.
288
+ */
289
+ declare class PermissionProtocolError extends Error {
290
+ /**
291
+ * Error code for programmatic handling.
292
+ */
293
+ readonly code: ErrorCode;
294
+ constructor(code: ErrorCode, message: string);
295
+ }
296
+
297
+ export { type ConfigureOptions, type ErrorCode, type ExecuteOptions, type ExecuteResult, type HashablePayloadV1, PermissionProtocolError, type PermissionReceipt, PermissionRouter, buildHashablePayload, canonicalize, computeHash };
package/dist/index.js ADDED
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ PermissionProtocolError: () => PermissionProtocolError,
24
+ PermissionRouter: () => PermissionRouter,
25
+ buildHashablePayload: () => buildHashablePayload,
26
+ canonicalize: () => canonicalize,
27
+ computeHash: () => computeHash
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/hash.ts
32
+ var import_crypto = require("crypto");
33
+ function canonicalize(obj) {
34
+ if (obj === null) {
35
+ return "null";
36
+ }
37
+ if (typeof obj !== "object") {
38
+ return JSON.stringify(obj);
39
+ }
40
+ if (Array.isArray(obj)) {
41
+ return "[" + obj.map(canonicalize).join(",") + "]";
42
+ }
43
+ const record = obj;
44
+ const sorted = Object.keys(record).filter((k) => record[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonicalize(record[k])}`);
45
+ return "{" + sorted.join(",") + "}";
46
+ }
47
+ function computeHash(payload) {
48
+ const canonical = canonicalize(payload);
49
+ const hash = (0, import_crypto.createHash)("sha256").update(canonical, "utf8").digest("hex");
50
+ return `sha256:${hash}`;
51
+ }
52
+ function buildHashablePayload(opts) {
53
+ return {
54
+ tool: opts.tool,
55
+ operation: opts.operation,
56
+ input: opts.input,
57
+ metadata: opts.metadata ?? null
58
+ };
59
+ }
60
+
61
+ // src/devApprovalMock.ts
62
+ function devApprovalMock(opts) {
63
+ console.warn(
64
+ '\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 PERMISSION PROTOCOL - DEV MODE \u2551\n\u2551 \u2551\n\u2551 This approval is NOT valid for production. \u2551\n\u2551 Real enforcement requires hosted Permission Protocol. \u2551\n\u2551 \u2551\n\u2551 Set mode: "hosted" for production use. \u2551\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n'
65
+ );
66
+ const hashablePayload = buildHashablePayload(opts);
67
+ const inputHash = computeHash(hashablePayload);
68
+ const receipt = {
69
+ receiptId: `dev_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
70
+ tenantId: opts.tenantId,
71
+ agentId: opts.agentId,
72
+ tool: opts.tool,
73
+ operation: opts.operation,
74
+ inputHash,
75
+ decision: "APPROVED",
76
+ reasonCodes: ["DEV_MODE_APPROVAL"],
77
+ approver: "dev_mock",
78
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
79
+ };
80
+ return {
81
+ status: "APPROVED",
82
+ receipt
83
+ };
84
+ }
85
+
86
+ // src/errors.ts
87
+ var PermissionProtocolError = class _PermissionProtocolError extends Error {
88
+ /**
89
+ * Error code for programmatic handling.
90
+ */
91
+ code;
92
+ constructor(code, message) {
93
+ super(`[PermissionProtocol:${code}] ${message}`);
94
+ this.name = "PermissionProtocolError";
95
+ this.code = code;
96
+ Object.setPrototypeOf(this, _PermissionProtocolError.prototype);
97
+ }
98
+ };
99
+
100
+ // src/mapping.ts
101
+ function mapHostedResponse(hosted, _opts) {
102
+ if (!hosted.receipt || !hosted.hostedStatus) {
103
+ throw new PermissionProtocolError(
104
+ "INVALID_RECEIPT",
105
+ "Hosted response missing required fields (receipt or hostedStatus)"
106
+ );
107
+ }
108
+ if (!hosted.receipt.receiptId) {
109
+ throw new PermissionProtocolError(
110
+ "INVALID_RECEIPT",
111
+ "Hosted response receipt missing receiptId"
112
+ );
113
+ }
114
+ return mapStatus(hosted.hostedStatus, hosted.receipt, hosted.result);
115
+ }
116
+ function mapStatus(status, receipt, result) {
117
+ switch (status) {
118
+ // === APPROVED ===
119
+ case "APPROVED":
120
+ return {
121
+ status: "APPROVED",
122
+ receipt: { ...receipt, decision: "APPROVED" },
123
+ result
124
+ };
125
+ // === REQUIRES_APPROVAL (direct) ===
126
+ case "REQUIRES_APPROVAL":
127
+ return {
128
+ status: "REQUIRES_APPROVAL",
129
+ receipt: { ...receipt, decision: "REQUIRES_APPROVAL" }
130
+ };
131
+ // === REQUIRES_FOUNDER_VETO -> REQUIRES_APPROVAL + reason code ===
132
+ case "REQUIRES_FOUNDER_VETO":
133
+ return {
134
+ status: "REQUIRES_APPROVAL",
135
+ receipt: {
136
+ ...receipt,
137
+ decision: "REQUIRES_APPROVAL",
138
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "REQUIRES_FOUNDER_VETO")
139
+ }
140
+ };
141
+ // === EXECUTING -> REQUIRES_APPROVAL + reason code ===
142
+ case "EXECUTING":
143
+ return {
144
+ status: "REQUIRES_APPROVAL",
145
+ receipt: {
146
+ ...receipt,
147
+ decision: "REQUIRES_APPROVAL",
148
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "EXECUTING_PENDING_RESULT")
149
+ }
150
+ };
151
+ // === PENDING_DECISION -> REQUIRES_APPROVAL + reason code ===
152
+ case "PENDING_DECISION":
153
+ return {
154
+ status: "REQUIRES_APPROVAL",
155
+ receipt: {
156
+ ...receipt,
157
+ decision: "REQUIRES_APPROVAL",
158
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "PENDING_DECISION")
159
+ }
160
+ };
161
+ // === DENIED (direct) ===
162
+ case "DENIED":
163
+ return {
164
+ status: "DENIED",
165
+ receipt: { ...receipt, decision: "DENIED" }
166
+ };
167
+ // === EXPIRED -> DENIED + reason code ===
168
+ case "EXPIRED":
169
+ return {
170
+ status: "DENIED",
171
+ receipt: {
172
+ ...receipt,
173
+ decision: "DENIED",
174
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "APPROVAL_EXPIRED")
175
+ }
176
+ };
177
+ // === ERROR -> DENIED + reason code ===
178
+ case "ERROR":
179
+ return {
180
+ status: "DENIED",
181
+ receipt: {
182
+ ...receipt,
183
+ decision: "DENIED",
184
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "EXECUTION_ERROR")
185
+ }
186
+ };
187
+ // === Unknown status -> fail closed ===
188
+ default:
189
+ throw new PermissionProtocolError(
190
+ "EXECUTION_UNAUTHORIZED",
191
+ `Unknown status from Permission Protocol: ${status}`
192
+ );
193
+ }
194
+ }
195
+ function ensureReasonCode(codes, code) {
196
+ if (codes.includes(code)) {
197
+ return codes;
198
+ }
199
+ return [...codes, code];
200
+ }
201
+
202
+ // src/client.ts
203
+ var DEFAULT_TIMEOUT_MS = 1e4;
204
+ var DEFAULT_SDK_VERSION = "0.1.0-alpha.1";
205
+ var DEFAULT_PROTOCOL_VERSION = 1;
206
+ async function fetchHostedPP(opts, body, headers) {
207
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
208
+ const sdkVersion = opts.sdkVersion ?? DEFAULT_SDK_VERSION;
209
+ const protocolVersion = opts.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
210
+ const controller = new AbortController();
211
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
212
+ try {
213
+ const response = await fetch(`${opts.endpoint}/api/permission/v1/execute`, {
214
+ method: "POST",
215
+ headers: {
216
+ "Content-Type": "application/json",
217
+ "Authorization": `Bearer ${opts.apiKey}`,
218
+ "X-PP-SDK-Version": sdkVersion,
219
+ "X-PP-Protocol-Version": String(protocolVersion),
220
+ "X-PP-Tenant-Id": headers.tenantId,
221
+ "X-PP-Agent-Id": headers.agentId
222
+ },
223
+ body: JSON.stringify(body),
224
+ signal: controller.signal
225
+ });
226
+ clearTimeout(timeoutId);
227
+ if (!response.ok) {
228
+ throw new PermissionProtocolError(
229
+ "EXECUTION_UNAUTHORIZED",
230
+ `Permission Protocol returned ${response.status}`
231
+ );
232
+ }
233
+ let hosted;
234
+ try {
235
+ hosted = await response.json();
236
+ } catch {
237
+ throw new PermissionProtocolError(
238
+ "EXECUTION_UNAUTHORIZED",
239
+ "Permission Protocol returned malformed response"
240
+ );
241
+ }
242
+ return hosted;
243
+ } catch (err) {
244
+ clearTimeout(timeoutId);
245
+ if (err instanceof PermissionProtocolError) {
246
+ throw err;
247
+ }
248
+ const error = err;
249
+ const message = error.name === "AbortError" ? "Permission Protocol request timed out" : "Permission Protocol unreachable";
250
+ throw new PermissionProtocolError("EXECUTION_UNAUTHORIZED", message);
251
+ }
252
+ }
253
+
254
+ // src/router.ts
255
+ var config = null;
256
+ var PermissionRouter = {
257
+ /**
258
+ * Configure the Permission Protocol client.
259
+ *
260
+ * Must be called before execute() in hosted mode.
261
+ *
262
+ * @param opts - Configuration options
263
+ */
264
+ configure(opts) {
265
+ config = opts;
266
+ },
267
+ /**
268
+ * Request authorization for an action.
269
+ *
270
+ * Returns a decision + receipt. Does NOT execute the action.
271
+ *
272
+ * @param opts - Execute options
273
+ * @returns Promise resolving to decision (APPROVED, REQUIRES_APPROVAL, or DENIED) with receipt
274
+ * @throws PermissionProtocolError on network failure, timeout, or invalid response
275
+ */
276
+ async execute(opts) {
277
+ if (opts.mode === "dev") {
278
+ return devApprovalMock(opts);
279
+ }
280
+ if (!config) {
281
+ throw new PermissionProtocolError(
282
+ "MISCONFIGURED",
283
+ "Permission Protocol client not configured. Call PermissionRouter.configure() first."
284
+ );
285
+ }
286
+ const hashablePayload = buildHashablePayload(opts);
287
+ const inputHash = computeHash(hashablePayload);
288
+ const body = {
289
+ tenantId: opts.tenantId,
290
+ actor: { agentId: opts.agentId },
291
+ intent: {
292
+ name: `${opts.tool}:${opts.operation}`,
293
+ summary: `${opts.tool} ${opts.operation}`,
294
+ category: opts.tool
295
+ },
296
+ action: {
297
+ tool: opts.tool,
298
+ operation: opts.operation,
299
+ parameters: opts.input
300
+ },
301
+ context: {
302
+ environment: "production",
303
+ reversibility: opts.reversibility ?? "UNKNOWN",
304
+ metadata: opts.metadata
305
+ },
306
+ hashes: { inputHash }
307
+ };
308
+ const hosted = await fetchHostedPP(
309
+ {
310
+ endpoint: config.endpoint,
311
+ apiKey: config.apiKey,
312
+ timeoutMs: config.timeoutMs,
313
+ protocolVersion: config.protocolVersion,
314
+ sdkVersion: config.sdkVersion
315
+ },
316
+ body,
317
+ {
318
+ tenantId: opts.tenantId,
319
+ agentId: opts.agentId
320
+ }
321
+ );
322
+ const result = mapHostedResponse(hosted, opts);
323
+ if (config.verifyReceipt) {
324
+ const valid = await config.verifyReceipt(result.receipt);
325
+ if (!valid) {
326
+ throw new PermissionProtocolError(
327
+ "INVALID_RECEIPT",
328
+ "Receipt verification failed"
329
+ );
330
+ }
331
+ }
332
+ return result;
333
+ },
334
+ /**
335
+ * Check if the client is configured.
336
+ * Useful for testing and debugging.
337
+ */
338
+ isConfigured() {
339
+ return config !== null;
340
+ },
341
+ /**
342
+ * Reset configuration (for testing).
343
+ * @internal
344
+ */
345
+ _reset() {
346
+ config = null;
347
+ }
348
+ };
349
+ // Annotate the CommonJS export names for ESM import in node:
350
+ 0 && (module.exports = {
351
+ PermissionProtocolError,
352
+ PermissionRouter,
353
+ buildHashablePayload,
354
+ canonicalize,
355
+ computeHash
356
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,325 @@
1
+ // src/hash.ts
2
+ import { createHash } from "crypto";
3
+ function canonicalize(obj) {
4
+ if (obj === null) {
5
+ return "null";
6
+ }
7
+ if (typeof obj !== "object") {
8
+ return JSON.stringify(obj);
9
+ }
10
+ if (Array.isArray(obj)) {
11
+ return "[" + obj.map(canonicalize).join(",") + "]";
12
+ }
13
+ const record = obj;
14
+ const sorted = Object.keys(record).filter((k) => record[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonicalize(record[k])}`);
15
+ return "{" + sorted.join(",") + "}";
16
+ }
17
+ function computeHash(payload) {
18
+ const canonical = canonicalize(payload);
19
+ const hash = createHash("sha256").update(canonical, "utf8").digest("hex");
20
+ return `sha256:${hash}`;
21
+ }
22
+ function buildHashablePayload(opts) {
23
+ return {
24
+ tool: opts.tool,
25
+ operation: opts.operation,
26
+ input: opts.input,
27
+ metadata: opts.metadata ?? null
28
+ };
29
+ }
30
+
31
+ // src/devApprovalMock.ts
32
+ function devApprovalMock(opts) {
33
+ console.warn(
34
+ '\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 PERMISSION PROTOCOL - DEV MODE \u2551\n\u2551 \u2551\n\u2551 This approval is NOT valid for production. \u2551\n\u2551 Real enforcement requires hosted Permission Protocol. \u2551\n\u2551 \u2551\n\u2551 Set mode: "hosted" for production use. \u2551\n\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D\n'
35
+ );
36
+ const hashablePayload = buildHashablePayload(opts);
37
+ const inputHash = computeHash(hashablePayload);
38
+ const receipt = {
39
+ receiptId: `dev_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`,
40
+ tenantId: opts.tenantId,
41
+ agentId: opts.agentId,
42
+ tool: opts.tool,
43
+ operation: opts.operation,
44
+ inputHash,
45
+ decision: "APPROVED",
46
+ reasonCodes: ["DEV_MODE_APPROVAL"],
47
+ approver: "dev_mock",
48
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
49
+ };
50
+ return {
51
+ status: "APPROVED",
52
+ receipt
53
+ };
54
+ }
55
+
56
+ // src/errors.ts
57
+ var PermissionProtocolError = class _PermissionProtocolError extends Error {
58
+ /**
59
+ * Error code for programmatic handling.
60
+ */
61
+ code;
62
+ constructor(code, message) {
63
+ super(`[PermissionProtocol:${code}] ${message}`);
64
+ this.name = "PermissionProtocolError";
65
+ this.code = code;
66
+ Object.setPrototypeOf(this, _PermissionProtocolError.prototype);
67
+ }
68
+ };
69
+
70
+ // src/mapping.ts
71
+ function mapHostedResponse(hosted, _opts) {
72
+ if (!hosted.receipt || !hosted.hostedStatus) {
73
+ throw new PermissionProtocolError(
74
+ "INVALID_RECEIPT",
75
+ "Hosted response missing required fields (receipt or hostedStatus)"
76
+ );
77
+ }
78
+ if (!hosted.receipt.receiptId) {
79
+ throw new PermissionProtocolError(
80
+ "INVALID_RECEIPT",
81
+ "Hosted response receipt missing receiptId"
82
+ );
83
+ }
84
+ return mapStatus(hosted.hostedStatus, hosted.receipt, hosted.result);
85
+ }
86
+ function mapStatus(status, receipt, result) {
87
+ switch (status) {
88
+ // === APPROVED ===
89
+ case "APPROVED":
90
+ return {
91
+ status: "APPROVED",
92
+ receipt: { ...receipt, decision: "APPROVED" },
93
+ result
94
+ };
95
+ // === REQUIRES_APPROVAL (direct) ===
96
+ case "REQUIRES_APPROVAL":
97
+ return {
98
+ status: "REQUIRES_APPROVAL",
99
+ receipt: { ...receipt, decision: "REQUIRES_APPROVAL" }
100
+ };
101
+ // === REQUIRES_FOUNDER_VETO -> REQUIRES_APPROVAL + reason code ===
102
+ case "REQUIRES_FOUNDER_VETO":
103
+ return {
104
+ status: "REQUIRES_APPROVAL",
105
+ receipt: {
106
+ ...receipt,
107
+ decision: "REQUIRES_APPROVAL",
108
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "REQUIRES_FOUNDER_VETO")
109
+ }
110
+ };
111
+ // === EXECUTING -> REQUIRES_APPROVAL + reason code ===
112
+ case "EXECUTING":
113
+ return {
114
+ status: "REQUIRES_APPROVAL",
115
+ receipt: {
116
+ ...receipt,
117
+ decision: "REQUIRES_APPROVAL",
118
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "EXECUTING_PENDING_RESULT")
119
+ }
120
+ };
121
+ // === PENDING_DECISION -> REQUIRES_APPROVAL + reason code ===
122
+ case "PENDING_DECISION":
123
+ return {
124
+ status: "REQUIRES_APPROVAL",
125
+ receipt: {
126
+ ...receipt,
127
+ decision: "REQUIRES_APPROVAL",
128
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "PENDING_DECISION")
129
+ }
130
+ };
131
+ // === DENIED (direct) ===
132
+ case "DENIED":
133
+ return {
134
+ status: "DENIED",
135
+ receipt: { ...receipt, decision: "DENIED" }
136
+ };
137
+ // === EXPIRED -> DENIED + reason code ===
138
+ case "EXPIRED":
139
+ return {
140
+ status: "DENIED",
141
+ receipt: {
142
+ ...receipt,
143
+ decision: "DENIED",
144
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "APPROVAL_EXPIRED")
145
+ }
146
+ };
147
+ // === ERROR -> DENIED + reason code ===
148
+ case "ERROR":
149
+ return {
150
+ status: "DENIED",
151
+ receipt: {
152
+ ...receipt,
153
+ decision: "DENIED",
154
+ reasonCodes: ensureReasonCode(receipt.reasonCodes, "EXECUTION_ERROR")
155
+ }
156
+ };
157
+ // === Unknown status -> fail closed ===
158
+ default:
159
+ throw new PermissionProtocolError(
160
+ "EXECUTION_UNAUTHORIZED",
161
+ `Unknown status from Permission Protocol: ${status}`
162
+ );
163
+ }
164
+ }
165
+ function ensureReasonCode(codes, code) {
166
+ if (codes.includes(code)) {
167
+ return codes;
168
+ }
169
+ return [...codes, code];
170
+ }
171
+
172
+ // src/client.ts
173
+ var DEFAULT_TIMEOUT_MS = 1e4;
174
+ var DEFAULT_SDK_VERSION = "0.1.0-alpha.1";
175
+ var DEFAULT_PROTOCOL_VERSION = 1;
176
+ async function fetchHostedPP(opts, body, headers) {
177
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
178
+ const sdkVersion = opts.sdkVersion ?? DEFAULT_SDK_VERSION;
179
+ const protocolVersion = opts.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
180
+ const controller = new AbortController();
181
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
182
+ try {
183
+ const response = await fetch(`${opts.endpoint}/api/permission/v1/execute`, {
184
+ method: "POST",
185
+ headers: {
186
+ "Content-Type": "application/json",
187
+ "Authorization": `Bearer ${opts.apiKey}`,
188
+ "X-PP-SDK-Version": sdkVersion,
189
+ "X-PP-Protocol-Version": String(protocolVersion),
190
+ "X-PP-Tenant-Id": headers.tenantId,
191
+ "X-PP-Agent-Id": headers.agentId
192
+ },
193
+ body: JSON.stringify(body),
194
+ signal: controller.signal
195
+ });
196
+ clearTimeout(timeoutId);
197
+ if (!response.ok) {
198
+ throw new PermissionProtocolError(
199
+ "EXECUTION_UNAUTHORIZED",
200
+ `Permission Protocol returned ${response.status}`
201
+ );
202
+ }
203
+ let hosted;
204
+ try {
205
+ hosted = await response.json();
206
+ } catch {
207
+ throw new PermissionProtocolError(
208
+ "EXECUTION_UNAUTHORIZED",
209
+ "Permission Protocol returned malformed response"
210
+ );
211
+ }
212
+ return hosted;
213
+ } catch (err) {
214
+ clearTimeout(timeoutId);
215
+ if (err instanceof PermissionProtocolError) {
216
+ throw err;
217
+ }
218
+ const error = err;
219
+ const message = error.name === "AbortError" ? "Permission Protocol request timed out" : "Permission Protocol unreachable";
220
+ throw new PermissionProtocolError("EXECUTION_UNAUTHORIZED", message);
221
+ }
222
+ }
223
+
224
+ // src/router.ts
225
+ var config = null;
226
+ var PermissionRouter = {
227
+ /**
228
+ * Configure the Permission Protocol client.
229
+ *
230
+ * Must be called before execute() in hosted mode.
231
+ *
232
+ * @param opts - Configuration options
233
+ */
234
+ configure(opts) {
235
+ config = opts;
236
+ },
237
+ /**
238
+ * Request authorization for an action.
239
+ *
240
+ * Returns a decision + receipt. Does NOT execute the action.
241
+ *
242
+ * @param opts - Execute options
243
+ * @returns Promise resolving to decision (APPROVED, REQUIRES_APPROVAL, or DENIED) with receipt
244
+ * @throws PermissionProtocolError on network failure, timeout, or invalid response
245
+ */
246
+ async execute(opts) {
247
+ if (opts.mode === "dev") {
248
+ return devApprovalMock(opts);
249
+ }
250
+ if (!config) {
251
+ throw new PermissionProtocolError(
252
+ "MISCONFIGURED",
253
+ "Permission Protocol client not configured. Call PermissionRouter.configure() first."
254
+ );
255
+ }
256
+ const hashablePayload = buildHashablePayload(opts);
257
+ const inputHash = computeHash(hashablePayload);
258
+ const body = {
259
+ tenantId: opts.tenantId,
260
+ actor: { agentId: opts.agentId },
261
+ intent: {
262
+ name: `${opts.tool}:${opts.operation}`,
263
+ summary: `${opts.tool} ${opts.operation}`,
264
+ category: opts.tool
265
+ },
266
+ action: {
267
+ tool: opts.tool,
268
+ operation: opts.operation,
269
+ parameters: opts.input
270
+ },
271
+ context: {
272
+ environment: "production",
273
+ reversibility: opts.reversibility ?? "UNKNOWN",
274
+ metadata: opts.metadata
275
+ },
276
+ hashes: { inputHash }
277
+ };
278
+ const hosted = await fetchHostedPP(
279
+ {
280
+ endpoint: config.endpoint,
281
+ apiKey: config.apiKey,
282
+ timeoutMs: config.timeoutMs,
283
+ protocolVersion: config.protocolVersion,
284
+ sdkVersion: config.sdkVersion
285
+ },
286
+ body,
287
+ {
288
+ tenantId: opts.tenantId,
289
+ agentId: opts.agentId
290
+ }
291
+ );
292
+ const result = mapHostedResponse(hosted, opts);
293
+ if (config.verifyReceipt) {
294
+ const valid = await config.verifyReceipt(result.receipt);
295
+ if (!valid) {
296
+ throw new PermissionProtocolError(
297
+ "INVALID_RECEIPT",
298
+ "Receipt verification failed"
299
+ );
300
+ }
301
+ }
302
+ return result;
303
+ },
304
+ /**
305
+ * Check if the client is configured.
306
+ * Useful for testing and debugging.
307
+ */
308
+ isConfigured() {
309
+ return config !== null;
310
+ },
311
+ /**
312
+ * Reset configuration (for testing).
313
+ * @internal
314
+ */
315
+ _reset() {
316
+ config = null;
317
+ }
318
+ };
319
+ export {
320
+ PermissionProtocolError,
321
+ PermissionRouter,
322
+ buildHashablePayload,
323
+ canonicalize,
324
+ computeHash
325
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@permission-protocol/sdk",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Standardized authorization and receipts for autonomous AI actions",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "license": "Apache-2.0",
19
+ "homepage": "https://github.com/permission-protocol/permission-protocol",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/permission-protocol/permission-protocol"
23
+ },
24
+ "keywords": [
25
+ "ai",
26
+ "agents",
27
+ "authorization",
28
+ "receipts",
29
+ "audit",
30
+ "human-in-the-loop",
31
+ "ci",
32
+ "governance"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "test:golden": "vitest run -t 'HashablePayload v1 conformance'",
42
+ "lint": "eslint src --ext .ts",
43
+ "prepublishOnly": "npm run build"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.0.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.0.0",
49
+ "vitest": "^1.0.0"
50
+ }
51
+ }