jev-layer 0.1.0 → 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 (88) hide show
  1. package/CHANGELOG.md +73 -34
  2. package/CONTRIBUTING.md +146 -146
  3. package/LICENSE +21 -21
  4. package/README.md +125 -126
  5. package/README.ru.md +140 -117
  6. package/README.zh-CN.md +139 -117
  7. package/RELEASE.md +53 -53
  8. package/SECURITY.md +64 -56
  9. package/bin/jev.mjs +215 -213
  10. package/config/codex.mcp.toml +8 -8
  11. package/config/generic-mcp.json +13 -13
  12. package/config/hermes.mcp.yaml +9 -9
  13. package/config/jev.example.json +23 -22
  14. package/config/omp.mcp.json +14 -14
  15. package/config/providers.env.example +13 -13
  16. package/docs/AGENT-IMPLEMENTATION.md +306 -0
  17. package/docs/COMPATIBILITY.md +24 -15
  18. package/docs/HERMES-LOCAL-AGENT-HANDOFF.ru.md +187 -0
  19. package/docs/PROVIDERS.md +109 -0
  20. package/docs/SCHEMA-VERSIONING.md +101 -94
  21. package/docs/architecture.svg +77 -0
  22. package/docs/jev-layer-banner.svg +38 -0
  23. package/examples/capabilities.json +39 -39
  24. package/examples/route-request.json +28 -28
  25. package/integrations/codex/.codex-plugin/plugin.json +19 -19
  26. package/integrations/codex/.mcp.json +11 -11
  27. package/integrations/codex/AGENTS.md +1 -1
  28. package/integrations/codex/run-mcp.mjs +9 -9
  29. package/integrations/codex/skills/jev-route/SKILL.md +17 -17
  30. package/integrations/hermes/__init__.py +156 -51
  31. package/integrations/hermes/plugin.yaml +11 -5
  32. package/integrations/hermes/schemas.py +120 -12
  33. package/integrations/omp/extension.js +106 -105
  34. package/integrations/template/README.md +10 -10
  35. package/integrations/template/adapter.mjs +87 -87
  36. package/package.json +60 -59
  37. package/scripts/benchmark.mjs +35 -35
  38. package/scripts/browser-benchmark.mjs +92 -92
  39. package/scripts/browser-e2e.mjs +117 -117
  40. package/scripts/capability-e2e.mjs +31 -31
  41. package/scripts/clean-install-smoke.mjs +176 -162
  42. package/scripts/codex-mcp-smoke.mjs +119 -119
  43. package/scripts/context-filter-e2e.mjs +45 -45
  44. package/scripts/fail-open-smoke.mjs +120 -120
  45. package/scripts/feature-flags-smoke.mjs +46 -46
  46. package/scripts/mcp-receipt-smoke.mjs +115 -98
  47. package/scripts/model-route-report.mjs +15 -0
  48. package/scripts/openrouter-choice.mjs +59 -59
  49. package/scripts/replay-eval.mjs +124 -124
  50. package/scripts/smoke.mjs +34 -34
  51. package/scripts/supervision-e2e.mjs +138 -109
  52. package/src/browser.mjs +583 -569
  53. package/src/cli.mjs +33 -33
  54. package/src/computer.mjs +196 -0
  55. package/src/config.mjs +86 -75
  56. package/src/context-filter.mjs +56 -56
  57. package/src/contract.mjs +74 -72
  58. package/src/discovery.mjs +65 -65
  59. package/src/hermes-adapter.mjs +134 -0
  60. package/src/images.mjs +98 -0
  61. package/src/mcp-server.mjs +313 -210
  62. package/src/model-route-metrics.mjs +89 -0
  63. package/src/model-routing.mjs +87 -0
  64. package/src/providers/demo.mjs +52 -52
  65. package/src/providers/index.mjs +12 -0
  66. package/src/providers/typesafe.mjs +206 -126
  67. package/src/receipts.mjs +228 -226
  68. package/src/registry.mjs +109 -109
  69. package/src/relevance-filter.mjs +99 -99
  70. package/src/route.mjs +228 -221
  71. package/src/shadow-compaction.mjs +170 -0
  72. package/src/supervision.mjs +268 -244
  73. package/test/browser.test.mjs +222 -199
  74. package/test/capability.test.mjs +54 -54
  75. package/test/computer.test.mjs +195 -0
  76. package/test/context-filter.test.mjs +65 -65
  77. package/test/hermes-adapter.test.mjs +63 -0
  78. package/test/image-fixture.mjs +2 -0
  79. package/test/images.test.mjs +85 -0
  80. package/test/model-route-metrics.test.mjs +61 -0
  81. package/test/model-routing.test.mjs +59 -0
  82. package/test/openrouter-provider.test.mjs +55 -55
  83. package/test/provider-transports.test.mjs +205 -0
  84. package/test/providers.test.mjs +125 -0
  85. package/test/receipts.test.mjs +71 -71
  86. package/test/route.test.mjs +86 -74
  87. package/test/shadow-compaction.test.mjs +67 -0
  88. package/test/supervision.test.mjs +117 -99
package/src/receipts.mjs CHANGED
@@ -1,226 +1,228 @@
1
- import { appendFile, mkdir, readFile } from "node:fs/promises";
2
- import { dirname, resolve } from "node:path";
3
- import { stableJson } from "./contract.mjs";
4
- import { sanitizeContext } from "./context-filter.mjs";
5
-
6
- const MAX_RESULT_BYTES = 8_000;
7
-
8
- export function replayCasePath(filePath = process.env.JEV_REPLAY_CASES) {
9
- return resolve(filePath || ".jev/replay/cases.jsonl");
10
- }
11
-
12
- export function buildRoutingCase({ request, decision, recordedAt = new Date().toISOString() }) {
13
- const correlationId = decision.correlation_id ?? decision.receipt?.correlation_id ?? decision.receipt?.request_id;
14
- if (!correlationId) throw new TypeError("decision correlation_id is required");
15
- return {
16
- record_type: "routing_case",
17
- schema_version: 1,
18
- case_id: correlationId,
19
- correlation_id: correlationId,
20
- recorded_at: recordedAt,
21
- harness: request.harness ?? "unknown",
22
- request: replayRequest(request),
23
- decision: decisionSummary(decision),
24
- };
25
- }
26
-
27
- export function buildExecutionReceipt({ request, decision, host, recordedAt = new Date().toISOString() }) {
28
- const correlationId = decision.correlation_id ?? decision.receipt?.correlation_id ?? decision.receipt?.request_id;
29
- if (!correlationId) throw new TypeError("decision correlation_id is required");
30
- if (!host || typeof host !== "object") throw new TypeError("host execution is required");
31
- const exitStatus = Number.isInteger(host.exit_status) ? host.exit_status : null;
32
- const status = typeof host.status === "string"
33
- ? host.status
34
- : exitStatus === null || exitStatus === 0 ? "completed" : "failed";
35
- return {
36
- record_type: "execution_receipt",
37
- schema_version: 1,
38
- receipt_id: `${correlationId}:execution`,
39
- correlation_id: correlationId,
40
- recorded_at: recordedAt,
41
- harness: host.harness ?? request.harness ?? "unknown",
42
- candidates: decision.candidates ?? [],
43
- selected: decision.selected ?? null,
44
- confidence: decision.confidence ?? null,
45
- probabilities: decision.probabilities ?? {},
46
- fallback: decision.fallback ?? null,
47
- jev: {
48
- correlation_id: correlationId,
49
- request_id: decision.receipt?.request_id ?? correlationId,
50
- provider: decision.receipt?.provider ?? null,
51
- latency_ms: decision.receipt?.latency_ms ?? null,
52
- cost_usd: decision.receipt?.cost_usd ?? decision.raw_jev?.usage?.cost ?? null,
53
- status: decision.status ?? null,
54
- },
55
- host: {
56
- capability_id: host.capability_id ?? decision.selected ?? null,
57
- status,
58
- result: boundedValue(host.result ?? host.host_result ?? null),
59
- error: boundedValue(host.error ?? null),
60
- exit_status: exitStatus,
61
- duration_ms: finiteNumber(host.duration_ms),
62
- started_at: host.started_at ?? null,
63
- completed_at: host.completed_at ?? recordedAt,
64
- browser: boundedValue(host.browser ?? null),
65
- },
66
- };
67
- }
68
-
69
- export async function appendRoutingCase({ path, request, decision }) {
70
- const record = buildRoutingCase({ request, decision });
71
- await appendRecord(path, record);
72
- return { record, path: replayCasePath(path) };
73
- }
74
-
75
- export async function appendExecutionReceipt({ path, request, decision, host }) {
76
- const record = buildExecutionReceipt({ request, decision, host });
77
- await appendRecord(path, record);
78
- return { record, path: replayCasePath(path) };
79
- }
80
-
81
- export function buildSupervisionReceipt({ request, result, recordedAt = new Date().toISOString() }) {
82
- const correlationId = result.receipt?.correlation_id;
83
- if (!correlationId) throw new TypeError("supervision result correlation_id is required");
84
- return {
85
- record_type: "supervision_case",
86
- schema_version: 1,
87
- case_id: correlationId,
88
- correlation_id: correlationId,
89
- recorded_at: recordedAt,
90
- harness: request.harness ?? "unknown",
91
- request: {
92
- schema_version: request.schema_version ?? 1,
93
- harness: request.harness ?? "unknown",
94
- intent: request.intent,
95
- context: sanitizeContext(request.context ?? {}),
96
- actor_permissions: request.actor_permissions ?? [],
97
- policy: request.policy ?? {},
98
- },
99
- supervision: {
100
- status: result.status ?? null,
101
- action: result.action ?? "continue",
102
- reason: result.reason ?? null,
103
- assessment: result.assessment ?? null,
104
- policy: result.policy ?? null,
105
- jev: {
106
- provider: result.receipt.provider ?? null,
107
- latency_ms: result.receipt.latency_ms ?? null,
108
- cost_usd: result.receipt.cost_usd ?? null,
109
- dimensions: result.receipt.dimensions ?? null,
110
- },
111
- },
112
- };
113
- }
114
-
115
- export async function appendSupervisionReceipt({ path, request, result }) {
116
- const record = buildSupervisionReceipt({ request, result });
117
- await appendRecord(path, record);
118
- return { record, path: replayCasePath(path) };
119
- }
120
-
121
- export async function readRoutingCases(path) {
122
- const resolved = replayCasePath(path);
123
- let text;
124
- try {
125
- text = await readFile(resolved, "utf8");
126
- } catch (error) {
127
- if (error?.code === "ENOENT") return [];
128
- throw error;
129
- }
130
- return text.split(/\r?\n/).filter(Boolean).flatMap((line, index) => {
131
- try {
132
- const record = JSON.parse(line);
133
- return record.record_type === "routing_case" ? [{ ...record, source_line: index + 1 }] : [];
134
- } catch {
135
- return [];
136
- }
137
- });
138
- }
139
-
140
- export async function readSupervisionCases(path) {
141
- const resolved = replayCasePath(path);
142
- let text;
143
- try {
144
- text = await readFile(resolved, "utf8");
145
- } catch (error) {
146
- if (error?.code === "ENOENT") return [];
147
- throw error;
148
- }
149
- return text.split(/\r?\n/).filter(Boolean).flatMap((line, index) => {
150
- try {
151
- const record = JSON.parse(line);
152
- return record.record_type === "supervision_case" ? [{ ...record, source_line: index + 1 }] : [];
153
- } catch {
154
- return [];
155
- }
156
- });
157
- }
158
-
159
- function replayRequest(request) {
160
- return {
161
- schema_version: request.schema_version ?? 1,
162
- harness: request.harness ?? "unknown",
163
- intent: request.intent,
164
- context: sanitizeContext(request.context ?? {}),
165
- actor: request.actor,
166
- actor_permissions: Array.isArray(request.actor_permissions) ? request.actor_permissions.filter((value) => typeof value === "string") : undefined,
167
- policy: request.policy && typeof request.policy === "object" ? request.policy : {},
168
- capabilities: Array.isArray(request.capabilities) ? request.capabilities.map(replayCapability) : [],
169
- };
170
- }
171
-
172
- function replayCapability(capability) {
173
- const risk = capability.risk?.level ?? capability.risk ?? "medium";
174
- return {
175
- id: capability.id,
176
- kind: capability.kind ?? capability.type ?? "tool",
177
- name: capability.name,
178
- description: capability.description,
179
- permissions: Array.isArray(capability.permissions) ? capability.permissions.filter((value) => typeof value === "string") : [],
180
- risk,
181
- available: capability.available,
182
- availability: capability.availability,
183
- source: typeof capability.source === "string" ? capability.source : null,
184
- verified: capability.verified === true,
185
- metadata: capability.metadata && typeof capability.metadata === "object" ? capability.metadata : {},
186
- policy: capability.policy,
187
- };
188
- }
189
-
190
- function decisionSummary(decision) {
191
- return {
192
- status: decision.status ?? null,
193
- selected: decision.selected ?? null,
194
- confidence: decision.confidence ?? null,
195
- probabilities: decision.probabilities ?? {},
196
- fallback: decision.fallback ?? null,
197
- candidates: decision.candidates ?? [],
198
- receipt: {
199
- correlation_id: decision.receipt?.correlation_id ?? decision.correlation_id ?? null,
200
- request_id: decision.receipt?.request_id ?? null,
201
- provider: decision.receipt?.provider ?? null,
202
- latency_ms: decision.receipt?.latency_ms ?? null,
203
- cost_usd: decision.receipt?.cost_usd ?? decision.raw_jev?.usage?.cost ?? null,
204
- candidate_count: decision.receipt?.candidate_count ?? null,
205
- context_bytes: decision.receipt?.context_bytes ?? null,
206
- },
207
- };
208
- }
209
-
210
- async function appendRecord(path, record) {
211
- const resolved = replayCasePath(path);
212
- await mkdir(dirname(resolved), { recursive: true });
213
- await appendFile(resolved, `${stableJson(record)}\n`, "utf8");
214
- }
215
-
216
- function finiteNumber(value) {
217
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
218
- }
219
-
220
- function boundedValue(value) {
221
- if (value === null || value === undefined) return null;
222
- if (typeof value === "string") return value.length > MAX_RESULT_BYTES ? `${value.slice(0, MAX_RESULT_BYTES)}…` : value;
223
- const sanitized = sanitizeContext(value);
224
- const encoded = stableJson(sanitized);
225
- return encoded.length > MAX_RESULT_BYTES ? `${encoded.slice(0, MAX_RESULT_BYTES)}…` : sanitized;
226
- }
1
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { stableJson } from "./contract.mjs";
4
+ import { sanitizeContext } from "./context-filter.mjs";
5
+
6
+ const MAX_RESULT_BYTES = 8_000;
7
+
8
+ export function replayCasePath(filePath = process.env.JEV_REPLAY_CASES) {
9
+ return resolve(filePath || ".jev/replay/cases.jsonl");
10
+ }
11
+
12
+ export function buildRoutingCase({ request, decision, recordedAt = new Date().toISOString() }) {
13
+ const correlationId = decision.correlation_id ?? decision.receipt?.correlation_id ?? decision.receipt?.request_id;
14
+ if (!correlationId) throw new TypeError("decision correlation_id is required");
15
+ return {
16
+ record_type: "routing_case",
17
+ schema_version: 1,
18
+ case_id: correlationId,
19
+ correlation_id: correlationId,
20
+ recorded_at: recordedAt,
21
+ harness: request.harness ?? "unknown",
22
+ request: replayRequest(request),
23
+ decision: decisionSummary(decision),
24
+ };
25
+ }
26
+
27
+ export function buildExecutionReceipt({ request, decision, host, recordedAt = new Date().toISOString() }) {
28
+ const correlationId = decision.correlation_id ?? decision.receipt?.correlation_id ?? decision.receipt?.request_id;
29
+ if (!correlationId) throw new TypeError("decision correlation_id is required");
30
+ if (!host || typeof host !== "object") throw new TypeError("host execution is required");
31
+ const exitStatus = Number.isInteger(host.exit_status) ? host.exit_status : null;
32
+ const status = typeof host.status === "string"
33
+ ? host.status
34
+ : exitStatus === null || exitStatus === 0 ? "completed" : "failed";
35
+ return {
36
+ record_type: "execution_receipt",
37
+ schema_version: 1,
38
+ receipt_id: `${correlationId}:execution`,
39
+ correlation_id: correlationId,
40
+ recorded_at: recordedAt,
41
+ harness: host.harness ?? request.harness ?? "unknown",
42
+ candidates: decision.candidates ?? [],
43
+ selected: decision.selected ?? null,
44
+ confidence: decision.confidence ?? null,
45
+ probabilities: decision.probabilities ?? {},
46
+ fallback: decision.fallback ?? null,
47
+ jev: {
48
+ correlation_id: correlationId,
49
+ request_id: decision.receipt?.request_id ?? correlationId,
50
+ provider: decision.receipt?.provider ?? null,
51
+ latency_ms: decision.receipt?.latency_ms ?? null,
52
+ cost_usd: decision.receipt?.cost_usd ?? decision.raw_jev?.usage?.cost ?? null,
53
+ status: decision.status ?? null,
54
+ },
55
+ host: {
56
+ capability_id: host.capability_id ?? decision.selected ?? null,
57
+ status,
58
+ result: boundedValue(host.result ?? host.host_result ?? null),
59
+ error: boundedValue(host.error ?? null),
60
+ exit_status: exitStatus,
61
+ duration_ms: finiteNumber(host.duration_ms),
62
+ started_at: host.started_at ?? null,
63
+ completed_at: host.completed_at ?? recordedAt,
64
+ browser: boundedValue(host.browser ?? null),
65
+ },
66
+ };
67
+ }
68
+
69
+ export async function appendRoutingCase({ path, request, decision }) {
70
+ const record = buildRoutingCase({ request, decision });
71
+ await appendRecord(path, record);
72
+ return { record, path: replayCasePath(path) };
73
+ }
74
+
75
+ export async function appendExecutionReceipt({ path, request, decision, host }) {
76
+ const record = buildExecutionReceipt({ request, decision, host });
77
+ await appendRecord(path, record);
78
+ return { record, path: replayCasePath(path) };
79
+ }
80
+
81
+ export function buildSupervisionReceipt({ request, result, recordedAt = new Date().toISOString() }) {
82
+ const correlationId = result.receipt?.correlation_id;
83
+ if (!correlationId) throw new TypeError("supervision result correlation_id is required");
84
+ return {
85
+ record_type: "supervision_case",
86
+ schema_version: 1,
87
+ case_id: correlationId,
88
+ correlation_id: correlationId,
89
+ recorded_at: recordedAt,
90
+ harness: request.harness ?? "unknown",
91
+ request: {
92
+ schema_version: request.schema_version ?? 1,
93
+ harness: request.harness ?? "unknown",
94
+ intent: request.intent,
95
+ context: sanitizeContext(request.context ?? {}),
96
+ actor_permissions: request.actor_permissions ?? [],
97
+ policy: request.policy ?? {},
98
+ },
99
+ supervision: {
100
+ status: result.status ?? null,
101
+ action: result.action ?? "continue",
102
+ reason: result.reason ?? null,
103
+ assessment: result.assessment ?? null,
104
+ evidence_state: result.evidence_state ?? request.context?.supervision?.evidence_state ?? null,
105
+ policy: result.policy ?? null,
106
+ jev: {
107
+ provider: result.receipt.provider ?? null,
108
+ latency_ms: result.receipt.latency_ms ?? null,
109
+ cost_usd: result.receipt.cost_usd ?? null,
110
+ dimensions: result.receipt.dimensions ?? null,
111
+ },
112
+ },
113
+ };
114
+ }
115
+
116
+ export async function appendSupervisionReceipt({ path, request, result }) {
117
+ const record = buildSupervisionReceipt({ request, result });
118
+ await appendRecord(path, record);
119
+ return { record, path: replayCasePath(path) };
120
+ }
121
+
122
+ export async function readRoutingCases(path) {
123
+ const resolved = replayCasePath(path);
124
+ let text;
125
+ try {
126
+ text = await readFile(resolved, "utf8");
127
+ } catch (error) {
128
+ if (error?.code === "ENOENT") return [];
129
+ throw error;
130
+ }
131
+ return text.split(/\r?\n/).filter(Boolean).flatMap((line, index) => {
132
+ try {
133
+ const record = JSON.parse(line);
134
+ return record.record_type === "routing_case" ? [{ ...record, source_line: index + 1 }] : [];
135
+ } catch {
136
+ return [];
137
+ }
138
+ });
139
+ }
140
+
141
+ export async function readSupervisionCases(path) {
142
+ const resolved = replayCasePath(path);
143
+ let text;
144
+ try {
145
+ text = await readFile(resolved, "utf8");
146
+ } catch (error) {
147
+ if (error?.code === "ENOENT") return [];
148
+ throw error;
149
+ }
150
+ return text.split(/\r?\n/).filter(Boolean).flatMap((line, index) => {
151
+ try {
152
+ const record = JSON.parse(line);
153
+ return record.record_type === "supervision_case" ? [{ ...record, source_line: index + 1 }] : [];
154
+ } catch {
155
+ return [];
156
+ }
157
+ });
158
+ }
159
+
160
+ function replayRequest(request) {
161
+ return {
162
+ schema_version: request.schema_version ?? 1,
163
+ harness: request.harness ?? "unknown",
164
+ intent: request.intent,
165
+ context: sanitizeContext(request.context ?? {}),
166
+ actor: request.actor,
167
+ actor_permissions: Array.isArray(request.actor_permissions) ? request.actor_permissions.filter((value) => typeof value === "string") : undefined,
168
+ policy: request.policy && typeof request.policy === "object" ? request.policy : {},
169
+ capabilities: Array.isArray(request.capabilities) ? request.capabilities.map(replayCapability) : [],
170
+ ...(request.assets_omitted || Object.keys(request.assets ?? {}).length ? { assets_omitted: true } : {}),
171
+ };
172
+ }
173
+
174
+ function replayCapability(capability) {
175
+ const risk = capability.risk?.level ?? capability.risk ?? "medium";
176
+ return {
177
+ id: capability.id,
178
+ kind: capability.kind ?? capability.type ?? "tool",
179
+ name: capability.name,
180
+ description: capability.description,
181
+ permissions: Array.isArray(capability.permissions) ? capability.permissions.filter((value) => typeof value === "string") : [],
182
+ risk,
183
+ available: capability.available,
184
+ availability: capability.availability,
185
+ source: typeof capability.source === "string" ? capability.source : null,
186
+ verified: capability.verified === true,
187
+ metadata: capability.metadata && typeof capability.metadata === "object" ? capability.metadata : {},
188
+ policy: capability.policy,
189
+ };
190
+ }
191
+
192
+ function decisionSummary(decision) {
193
+ return {
194
+ status: decision.status ?? null,
195
+ selected: decision.selected ?? null,
196
+ confidence: decision.confidence ?? null,
197
+ probabilities: decision.probabilities ?? {},
198
+ fallback: decision.fallback ?? null,
199
+ candidates: decision.candidates ?? [],
200
+ receipt: {
201
+ correlation_id: decision.receipt?.correlation_id ?? decision.correlation_id ?? null,
202
+ request_id: decision.receipt?.request_id ?? null,
203
+ provider: decision.receipt?.provider ?? null,
204
+ latency_ms: decision.receipt?.latency_ms ?? null,
205
+ cost_usd: decision.receipt?.cost_usd ?? decision.raw_jev?.usage?.cost ?? null,
206
+ candidate_count: decision.receipt?.candidate_count ?? null,
207
+ context_bytes: decision.receipt?.context_bytes ?? null,
208
+ },
209
+ };
210
+ }
211
+
212
+ async function appendRecord(path, record) {
213
+ const resolved = replayCasePath(path);
214
+ await mkdir(dirname(resolved), { recursive: true });
215
+ await appendFile(resolved, `${stableJson(record)}\n`, "utf8");
216
+ }
217
+
218
+ function finiteNumber(value) {
219
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
220
+ }
221
+
222
+ function boundedValue(value) {
223
+ if (value === null || value === undefined) return null;
224
+ if (typeof value === "string") return value.length > MAX_RESULT_BYTES ? `${value.slice(0, MAX_RESULT_BYTES)}…` : value;
225
+ const sanitized = sanitizeContext(value);
226
+ const encoded = stableJson(sanitized);
227
+ return encoded.length > MAX_RESULT_BYTES ? `${encoded.slice(0, MAX_RESULT_BYTES)}…` : sanitized;
228
+ }