@irogane/kaji 0.2.0-beta.11 → 0.3.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.
Files changed (137) hide show
  1. package/README.md +112 -551
  2. package/dist/index.d.mts +253 -0
  3. package/dist/index.mjs +440 -0
  4. package/package.json +28 -166
  5. package/contracts/README.md +0 -14
  6. package/contracts/beta-core-v1.json +0 -52
  7. package/contracts/cli/init-cases-v1.json +0 -27
  8. package/contracts/errors/error-codes.json +0 -48
  9. package/contracts/errors/integration-recovery-v1.json +0 -127
  10. package/contracts/errors/provider-normalization.json +0 -111
  11. package/contracts/events/conformance-invalid.json +0 -20
  12. package/contracts/events/conformance.json +0 -511
  13. package/contracts/events/new-kaji-event-v1.schema.json +0 -1021
  14. package/contracts/events/stored-kaji-event-v1.schema.json +0 -1025
  15. package/contracts/feature-tiers-v1.json +0 -488
  16. package/contracts/integrations/abi-index-v1.json +0 -8
  17. package/contracts/integrations/conformance-invalid.json +0 -443
  18. package/contracts/integrations/conformance-valid.json +0 -121
  19. package/contracts/integrations/copy-provenance-v1.schema.json +0 -61
  20. package/contracts/integrations/echo-tool-abi-v1.json +0 -37
  21. package/contracts/integrations/github-api-conformance-v1.json +0 -644
  22. package/contracts/integrations/github-tool-abi-typescript-v1.json +0 -369
  23. package/contracts/integrations/github-tool-abi-v1.json +0 -146
  24. package/contracts/integrations/gmail-api-conformance-v1.json +0 -750
  25. package/contracts/integrations/gmail-tool-abi-v1.json +0 -62
  26. package/contracts/integrations/index.schema.json +0 -37
  27. package/contracts/integrations/manifest.schema.json +0 -119
  28. package/contracts/parity/expected-normalized.json +0 -4906
  29. package/contracts/parity/scenarios.json +0 -100
  30. package/contracts/parity/scenarios.schema.json +0 -214
  31. package/contracts/providers/cost-conformance.json +0 -112
  32. package/contracts/release/github-proof-v1.schema.json +0 -138
  33. package/contracts/release/gmail-proof-v1.schema.json +0 -138
  34. package/contracts/release/kaji-ts-consumer-handoff-v1.schema.json +0 -1289
  35. package/contracts/release/publisher-identity-receipt-v1.schema.json +0 -319
  36. package/contracts/release/typescript-onboarding-evidence-v1.schema.json +0 -740
  37. package/contracts/tools/conformance-invalid.json +0 -271
  38. package/contracts/tools/conformance-valid.json +0 -78
  39. package/contracts/tools/tool-schema-v1.schema.json +0 -18
  40. package/dist/anthropic.cjs +0 -1231
  41. package/dist/anthropic.cjs.map +0 -1
  42. package/dist/anthropic.d.cts +0 -26
  43. package/dist/anthropic.d.ts +0 -26
  44. package/dist/anthropic.js +0 -270
  45. package/dist/anthropic.js.map +0 -1
  46. package/dist/auth.cjs +0 -1507
  47. package/dist/auth.cjs.map +0 -1
  48. package/dist/auth.d.cts +0 -129
  49. package/dist/auth.d.ts +0 -129
  50. package/dist/auth.js +0 -1039
  51. package/dist/auth.js.map +0 -1
  52. package/dist/base-B9FRMcP8.d.cts +0 -140
  53. package/dist/base-nHQd1VtS.d.ts +0 -140
  54. package/dist/chunk-AAM33KAO.js +0 -4367
  55. package/dist/chunk-AAM33KAO.js.map +0 -1
  56. package/dist/chunk-KAJ6BM64.js +0 -153
  57. package/dist/chunk-KAJ6BM64.js.map +0 -1
  58. package/dist/chunk-KCAXIOZS.js +0 -308
  59. package/dist/chunk-KCAXIOZS.js.map +0 -1
  60. package/dist/chunk-LSJ4AVO2.js +0 -243
  61. package/dist/chunk-LSJ4AVO2.js.map +0 -1
  62. package/dist/chunk-TM7ZGOJX.js +0 -716
  63. package/dist/chunk-TM7ZGOJX.js.map +0 -1
  64. package/dist/cli/bin.d.ts +0 -2
  65. package/dist/cli/bin.js +0 -13
  66. package/dist/cli/bin.js.map +0 -1
  67. package/dist/cli/chunk-2RCWPRVY.js +0 -6277
  68. package/dist/cli/chunk-2RCWPRVY.js.map +0 -1
  69. package/dist/cli/chunk-SEBX54TR.js +0 -681
  70. package/dist/cli/chunk-SEBX54TR.js.map +0 -1
  71. package/dist/cli/index.d.ts +0 -232
  72. package/dist/cli/index.js +0 -11
  73. package/dist/cli/index.js.map +0 -1
  74. package/dist/cli/init-worker.d.ts +0 -2
  75. package/dist/cli/init-worker.js +0 -18
  76. package/dist/cli/init-worker.js.map +0 -1
  77. package/dist/cli/integration-copy-worker.js +0 -48
  78. package/dist/cli/integration-copy-worker.js.map +0 -1
  79. package/dist/cli/package-entry-cjs.cjs +0 -21
  80. package/dist/cli/package-entry-cjs.cjs.map +0 -1
  81. package/dist/cli/package-entry-cjs.d.cts +0 -2
  82. package/dist/cli/package-entry.d.ts +0 -2
  83. package/dist/cli/package-entry.js +0 -13
  84. package/dist/cli/package-entry.js.map +0 -1
  85. package/dist/context-BaFHrQHv.d.cts +0 -21
  86. package/dist/context-BaFHrQHv.d.ts +0 -21
  87. package/dist/context-C-YPY-GS.d.cts +0 -1538
  88. package/dist/context-C-YPY-GS.d.ts +0 -1538
  89. package/dist/index.cjs +0 -12346
  90. package/dist/index.cjs.map +0 -1
  91. package/dist/index.d.cts +0 -1560
  92. package/dist/index.d.ts +0 -1560
  93. package/dist/index.js +0 -7852
  94. package/dist/index.js.map +0 -1
  95. package/dist/integrations/github.cjs +0 -2092
  96. package/dist/integrations/github.cjs.map +0 -1
  97. package/dist/integrations/github.d.cts +0 -21
  98. package/dist/integrations/github.d.ts +0 -21
  99. package/dist/integrations/github.js +0 -2088
  100. package/dist/integrations/github.js.map +0 -1
  101. package/dist/integrations.cjs +0 -3370
  102. package/dist/integrations.cjs.map +0 -1
  103. package/dist/integrations.d.cts +0 -202
  104. package/dist/integrations.d.ts +0 -202
  105. package/dist/integrations.js +0 -2650
  106. package/dist/integrations.js.map +0 -1
  107. package/dist/observability-Cj--OkME.d.cts +0 -96
  108. package/dist/observability-Cj--OkME.d.ts +0 -96
  109. package/dist/openai.cjs +0 -1235
  110. package/dist/openai.cjs.map +0 -1
  111. package/dist/openai.d.cts +0 -32
  112. package/dist/openai.d.ts +0 -32
  113. package/dist/openai.js +0 -272
  114. package/dist/openai.js.map +0 -1
  115. package/dist/testing.cjs +0 -554
  116. package/dist/testing.cjs.map +0 -1
  117. package/dist/testing.d.cts +0 -43
  118. package/dist/testing.d.ts +0 -43
  119. package/dist/testing.js +0 -118
  120. package/dist/testing.js.map +0 -1
  121. package/registry/echo/index.ts +0 -53
  122. package/registry/echo/manifest.json +0 -53
  123. package/registry/github/LICENSE +0 -105
  124. package/registry/github/client.ts +0 -1727
  125. package/registry/github/index.ts +0 -263
  126. package/registry/github/manifest.json +0 -227
  127. package/registry/github/owner-fixtures.json +0 -10
  128. package/registry/github/tests/github.test.ts +0 -32
  129. package/registry/gmail/LICENSE +0 -105
  130. package/registry/gmail/client.ts +0 -548
  131. package/registry/gmail/index.ts +0 -165
  132. package/registry/gmail/manifest.json +0 -102
  133. package/registry/gmail/owner-fixtures.json +0 -10
  134. package/registry/gmail/tests/gmail.test.ts +0 -32
  135. package/registry/index.json +0 -21
  136. package/registry/index.schema.json +0 -37
  137. package/registry/schema.json +0 -119
@@ -1,165 +0,0 @@
1
- // This is YOUR Gmail integration. Edit it.
2
-
3
- import {
4
- Integration,
5
- type MetricsSink,
6
- type ToolExecutionContext,
7
- type ToolHandler,
8
- type ToolSpec,
9
- type TraceSink,
10
- } from "@irogane/kaji";
11
- import { createGmailRequester } from "@irogane/kaji/integrations";
12
-
13
- import { GmailClient } from "./client";
14
-
15
- export type SharedGmailClient = Pick<GmailClient, "listMessages" | "getMessage" | "sendMessage">;
16
-
17
- function parameters(
18
- properties: Readonly<Record<string, unknown>>,
19
- required: readonly string[],
20
- ): Record<string, unknown> {
21
- return {
22
- $schema: "https://json-schema.org/draft/2020-12/schema",
23
- type: "object",
24
- properties,
25
- required: [...required],
26
- additionalProperties: false,
27
- };
28
- }
29
-
30
- function specs(): readonly ToolSpec[] {
31
- return [
32
- {
33
- name: "list_messages",
34
- description:
35
- "List messages in the authenticated user's mailbox. Pass `page_token` from a prior result's `next_page_token` to page through results.",
36
- parameters: parameters(
37
- {
38
- query: { type: "string", minLength: 1, maxLength: 1_024 },
39
- max_results: { type: "integer", minimum: 1, maximum: 100, default: 10 },
40
- page_token: { type: "string", minLength: 1, maxLength: 2_048 },
41
- },
42
- [],
43
- ),
44
- risk: "read",
45
- parallel_safe: true,
46
- timeout_ms: 10_000,
47
- },
48
- {
49
- name: "get_message",
50
- description: "Get a message from the authenticated user's mailbox.",
51
- parameters: parameters(
52
- {
53
- message_id: { type: "string", minLength: 1, maxLength: 128 },
54
- },
55
- ["message_id"],
56
- ),
57
- risk: "read",
58
- parallel_safe: true,
59
- timeout_ms: 10_000,
60
- },
61
- {
62
- name: "send_message",
63
- description:
64
- "Send an email as the authenticated user. `raw` is the complete RFC 2822 message, base64url-encoded.",
65
- parameters: parameters(
66
- {
67
- raw: { type: "string", minLength: 1, maxLength: 1_048_576 },
68
- },
69
- ["raw"],
70
- ),
71
- risk: "external_effect",
72
- parallel_safe: false,
73
- timeout_ms: 15_000,
74
- },
75
- ];
76
- }
77
-
78
- function objectResult(value: unknown): Record<string, unknown> {
79
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
80
- throw new Error("Gmail client returned an invalid tool result");
81
- }
82
- return value as Record<string, unknown>;
83
- }
84
-
85
- function handler(client: SharedGmailClient, name: string): ToolHandler {
86
- return async (args, context) => {
87
- switch (name) {
88
- case "list_messages":
89
- return objectResult(
90
- await client.listMessages(context, {
91
- ...(args.query === undefined ? {} : { query: args.query as string }),
92
- ...(args.max_results === undefined ? {} : { maxResults: args.max_results as number }),
93
- ...(args.page_token === undefined ? {} : { pageToken: args.page_token as string }),
94
- }),
95
- );
96
- case "get_message":
97
- return objectResult(
98
- await client.getMessage(context, { messageId: args.message_id as string }),
99
- );
100
- case "send_message":
101
- return objectResult(await client.sendMessage(context, { raw: args.raw as string }));
102
- default:
103
- throw new Error("Unknown Gmail tool");
104
- }
105
- };
106
- }
107
-
108
- export function createSharedGmailToolBindings(
109
- client: SharedGmailClient,
110
- ): [ToolSpec, ToolHandler][] {
111
- return specs().map((spec) => [spec, handler(client, spec.name)]);
112
- }
113
-
114
- export class GmailIntegration extends Integration {
115
- readonly namespace = "gmail";
116
- private closeOwnedRequester: (() => void) | undefined;
117
-
118
- constructor(
119
- private readonly client: SharedGmailClient,
120
- closeOwnedRequester?: () => void,
121
- ) {
122
- super();
123
- this.closeOwnedRequester = closeOwnedRequester;
124
- }
125
-
126
- override tools(): [ToolSpec, ToolHandler][] {
127
- return createSharedGmailToolBindings(this.client);
128
- }
129
-
130
- close(): void {
131
- const close = this.closeOwnedRequester;
132
- if (close === undefined) return;
133
- close();
134
- this.closeOwnedRequester = undefined;
135
- }
136
- }
137
-
138
- export interface CreateGmailIntegrationOptions {
139
- readonly tokenFor: (context: ToolExecutionContext) => Promise<string>;
140
- readonly metricsSink?: MetricsSink;
141
- readonly traceSink?: TraceSink;
142
- }
143
-
144
- export function createGmailIntegration(options: CreateGmailIntegrationOptions): GmailIntegration {
145
- const { metricsSink, traceSink, ...clientOptions } = options;
146
- const http = createGmailRequester({ metricsSink, traceSink });
147
- return new GmailIntegration(new GmailClient({ ...clientOptions, http }), () => http.close());
148
- }
149
-
150
- function createGmailIntegrationForTest(client: SharedGmailClient): GmailIntegration {
151
- return new GmailIntegration(client);
152
- }
153
-
154
- const inspectionClient = new Proxy(
155
- {},
156
- {
157
- get: () => async () => {
158
- throw new Error("inspection dependencies must not execute");
159
- },
160
- },
161
- ) as SharedGmailClient;
162
-
163
- export function inspectIntegration(): GmailIntegration {
164
- return createGmailIntegrationForTest(inspectionClient);
165
- }
@@ -1,102 +0,0 @@
1
- {
2
- "name": "gmail",
3
- "version": "0.1.0",
4
- "namespace": "gmail",
5
- "description": "Mailbox-scoped Gmail read and send tools for the authenticated user.",
6
- "auth": {
7
- "kind": "oauth",
8
- "provider": "google",
9
- "clientIdEnv": "GOOGLE_CLIENT_ID",
10
- "clientSecretEnv": "GOOGLE_CLIENT_SECRET",
11
- "scopes": [
12
- "https://www.googleapis.com/auth/gmail.readonly",
13
- "https://www.googleapis.com/auth/gmail.send"
14
- ],
15
- "docs": "https://developers.google.com/gmail/api/auth/scopes"
16
- },
17
- "files": [
18
- "index.ts",
19
- "client.ts",
20
- "tests/gmail.test.ts",
21
- "owner-fixtures.json",
22
- "LICENSE"
23
- ],
24
- "tools": [
25
- {
26
- "name": "get_message",
27
- "description": "Get a message from the authenticated user's mailbox.",
28
- "parameters": {
29
- "$schema": "https://json-schema.org/draft/2020-12/schema",
30
- "type": "object",
31
- "properties": {
32
- "message_id": {
33
- "type": "string",
34
- "minLength": 1,
35
- "maxLength": 128
36
- }
37
- },
38
- "required": [
39
- "message_id"
40
- ],
41
- "additionalProperties": false
42
- },
43
- "risk": "read",
44
- "parallel_safe": true,
45
- "timeout_ms": 10000
46
- },
47
- {
48
- "name": "list_messages",
49
- "description": "List messages in the authenticated user's mailbox. Pass `page_token` from a prior result's `next_page_token` to page through results.",
50
- "parameters": {
51
- "$schema": "https://json-schema.org/draft/2020-12/schema",
52
- "type": "object",
53
- "properties": {
54
- "query": {
55
- "type": "string",
56
- "minLength": 1,
57
- "maxLength": 1024
58
- },
59
- "max_results": {
60
- "type": "integer",
61
- "minimum": 1,
62
- "maximum": 100,
63
- "default": 10
64
- },
65
- "page_token": {
66
- "type": "string",
67
- "minLength": 1,
68
- "maxLength": 2048
69
- }
70
- },
71
- "required": [],
72
- "additionalProperties": false
73
- },
74
- "risk": "read",
75
- "parallel_safe": true,
76
- "timeout_ms": 10000
77
- },
78
- {
79
- "name": "send_message",
80
- "description": "Send an email as the authenticated user. `raw` is the complete RFC 2822 message, base64url-encoded.",
81
- "parameters": {
82
- "$schema": "https://json-schema.org/draft/2020-12/schema",
83
- "type": "object",
84
- "properties": {
85
- "raw": {
86
- "type": "string",
87
- "minLength": 1,
88
- "maxLength": 1048576
89
- }
90
- },
91
- "required": [
92
- "raw"
93
- ],
94
- "additionalProperties": false
95
- },
96
- "risk": "external_effect",
97
- "parallel_safe": false,
98
- "timeout_ms": 15000
99
- }
100
- ],
101
- "peerDeps": {}
102
- }
@@ -1,10 +0,0 @@
1
- {
2
- "schemaVersion": "1.0.0",
3
- "outcomes": [
4
- { "name": "success", "expected": "success" },
5
- { "name": "missing_auth", "expected": "INTEGRATION_AUTH_REQUIRED" },
6
- { "name": "rate_limit", "expected": "INTEGRATION_RATE_LIMITED" },
7
- { "name": "approval_rejected", "expected": "APPROVAL_REJECTED" },
8
- { "name": "connection_lost_after_dispatch", "expected": "unknown" }
9
- ]
10
- }
@@ -1,32 +0,0 @@
1
- // This is YOUR no-network Gmail owner fixture test. Edit it.
2
-
3
- import { readFileSync } from "node:fs";
4
- import { describe, expect, it } from "vitest";
5
-
6
- const closedOutcomes = new Set([
7
- "success",
8
- "missing_auth",
9
- "rate_limit",
10
- "approval_rejected",
11
- "connection_lost_after_dispatch",
12
- ]);
13
-
14
- export function scriptedOutcome(name: string): string {
15
- if (!closedOutcomes.has(name)) throw new Error("unknown scripted Gmail outcome");
16
- const fixture = JSON.parse(
17
- readFileSync(new URL("../owner-fixtures.json", import.meta.url), "utf8"),
18
- ) as { outcomes: Array<{ name: string; expected: string }> };
19
- return fixture.outcomes.find((row) => row.name === name)!.expected;
20
- }
21
-
22
- describe("Gmail owner fixtures", () => {
23
- it("keeps scripted outcomes closed and network-free", () => {
24
- expect([...closedOutcomes].map((name) => [name, scriptedOutcome(name)])).toEqual([
25
- ["success", "success"],
26
- ["missing_auth", "INTEGRATION_AUTH_REQUIRED"],
27
- ["rate_limit", "INTEGRATION_RATE_LIMITED"],
28
- ["approval_rejected", "APPROVAL_REJECTED"],
29
- ["connection_lost_after_dispatch", "unknown"],
30
- ]);
31
- });
32
- });
@@ -1,21 +0,0 @@
1
- {
2
- "$schema": "./index.schema.json",
3
- "version": "0.1.0",
4
- "integrations": {
5
- "echo": {
6
- "manifest": "echo/manifest.json",
7
- "stability": "beta",
8
- "runtimes": ["python", "typescript"]
9
- },
10
- "gmail": {
11
- "manifest": "gmail/manifest.json",
12
- "stability": "beta",
13
- "runtimes": ["python", "typescript"]
14
- },
15
- "github": {
16
- "manifest": "github/manifest.json",
17
- "stability": "beta",
18
- "runtimes": ["python", "typescript"]
19
- }
20
- }
21
- }
@@ -1,37 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://kaji.dev/schemas/integration-index-v1.json",
4
- "title": "Kaji integration registry index",
5
- "type": "object",
6
- "required": ["$schema", "version", "integrations"],
7
- "additionalProperties": false,
8
- "properties": {
9
- "$schema": { "const": "./index.schema.json" },
10
- "version": {
11
- "type": "string",
12
- "pattern": "^\\d+\\.\\d+\\.\\d+$"
13
- },
14
- "integrations": {
15
- "type": "object",
16
- "propertyNames": { "pattern": "^[a-z][a-z0-9_-]*$" },
17
- "additionalProperties": {
18
- "type": "object",
19
- "required": ["manifest", "stability", "runtimes"],
20
- "additionalProperties": false,
21
- "properties": {
22
- "manifest": {
23
- "type": "string",
24
- "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*(?:^|/)\\.\\.?(?:/|$))[A-Za-z0-9_@.+-]+(?:/[A-Za-z0-9_@.+-]+)*$"
25
- },
26
- "stability": { "enum": ["experimental", "beta"] },
27
- "runtimes": {
28
- "type": "array",
29
- "minItems": 1,
30
- "uniqueItems": true,
31
- "items": { "enum": ["python", "typescript"] }
32
- }
33
- }
34
- }
35
- }
36
- }
37
- }
@@ -1,119 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://kaji.dev/schemas/integration-manifest-v1.json",
4
- "title": "Kaji integration manifest",
5
- "type": "object",
6
- "required": ["name", "version", "namespace", "description", "auth", "files", "tools"],
7
- "additionalProperties": false,
8
- "properties": {
9
- "name": {
10
- "type": "string",
11
- "maxLength": 128,
12
- "pattern": "^[a-z][a-z0-9_-]*$"
13
- },
14
- "version": {
15
- "type": "string",
16
- "pattern": "^\\d+\\.\\d+\\.\\d+$"
17
- },
18
- "namespace": {
19
- "type": "string",
20
- "pattern": "^[a-z][a-z0-9_]*$"
21
- },
22
- "description": {
23
- "type": "string",
24
- "minLength": 1
25
- },
26
- "auth": {
27
- "oneOf": [
28
- {
29
- "type": "object",
30
- "required": ["kind"],
31
- "properties": {
32
- "kind": { "const": "none" }
33
- },
34
- "additionalProperties": false
35
- },
36
- {
37
- "type": "object",
38
- "required": ["kind", "env"],
39
- "properties": {
40
- "kind": { "const": "env" },
41
- "env": {
42
- "type": "string",
43
- "pattern": "^[A-Z][A-Z0-9_]*$"
44
- },
45
- "optional": { "type": "boolean", "default": false },
46
- "docs": { "type": "string", "format": "uri" }
47
- },
48
- "additionalProperties": false
49
- },
50
- {
51
- "type": "object",
52
- "required": ["kind", "provider", "clientIdEnv", "scopes"],
53
- "properties": {
54
- "kind": { "const": "oauth" },
55
- "provider": { "enum": ["google"] },
56
- "clientIdEnv": {
57
- "type": "string",
58
- "pattern": "^[A-Z][A-Z0-9_]*$"
59
- },
60
- "clientSecretEnv": {
61
- "type": "string",
62
- "pattern": "^[A-Z][A-Z0-9_]*$"
63
- },
64
- "scopes": {
65
- "type": "array",
66
- "minItems": 1,
67
- "uniqueItems": true,
68
- "items": { "type": "string", "minLength": 1, "maxLength": 512 }
69
- },
70
- "docs": { "type": "string", "format": "uri" }
71
- },
72
- "additionalProperties": false
73
- }
74
- ]
75
- },
76
- "files": {
77
- "type": "array",
78
- "minItems": 1,
79
- "uniqueItems": true,
80
- "items": {
81
- "type": "string",
82
- "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*(?:^|/)\\.\\.?(?:/|$))[A-Za-z0-9_@.+-]+(?:/[A-Za-z0-9_@.+-]+)*$"
83
- }
84
- },
85
- "tools": {
86
- "type": "array",
87
- "minItems": 1,
88
- "uniqueItems": true,
89
- "items": {
90
- "type": "object",
91
- "required": ["name", "description", "parameters", "risk", "parallel_safe"],
92
- "additionalProperties": false,
93
- "properties": {
94
- "name": {
95
- "type": "string",
96
- "pattern": "^[a-z][a-z0-9_]*$"
97
- },
98
- "description": { "type": "string", "minLength": 1 },
99
- "parameters": { "type": "object" },
100
- "risk": {
101
- "enum": ["read", "write", "external_effect", "destructive", "admin"]
102
- },
103
- "parallel_safe": { "type": "boolean" },
104
- "timeout_ms": { "type": "integer", "minimum": 1 }
105
- }
106
- }
107
- },
108
- "extras": {
109
- "type": "array",
110
- "uniqueItems": true,
111
- "items": { "type": "string", "minLength": 1 }
112
- },
113
- "peerDeps": {
114
- "type": "object",
115
- "propertyNames": { "minLength": 1 },
116
- "additionalProperties": { "type": "string", "minLength": 1 }
117
- }
118
- }
119
- }