@nuxtseo/protocol 0.2.0 → 0.2.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.
@@ -458,6 +458,7 @@ export declare const accountTokenDataSchema: import("./core.js").CompatibleRespo
458
458
  "audit:read": "audit:read";
459
459
  "content:read": "content:read";
460
460
  "content:write": "content:write";
461
+ "feedback:write": "feedback:write";
461
462
  "page:read": "page:read";
462
463
  "page:write": "page:write";
463
464
  "research:read": "research:read";
@@ -507,6 +508,7 @@ export declare const accountTokenDataSchema: import("./core.js").CompatibleRespo
507
508
  "audit:read": "audit:read";
508
509
  "content:read": "content:read";
509
510
  "content:write": "content:write";
511
+ "feedback:write": "feedback:write";
510
512
  "page:read": "page:read";
511
513
  "page:write": "page:write";
512
514
  "research:read": "research:read";
@@ -558,6 +560,7 @@ export declare const accountTokenResponseSchema: import("./core.js").CompatibleR
558
560
  "audit:read": "audit:read";
559
561
  "content:read": "content:read";
560
562
  "content:write": "content:write";
563
+ "feedback:write": "feedback:write";
561
564
  "page:read": "page:read";
562
565
  "page:write": "page:write";
563
566
  "research:read": "research:read";
@@ -613,6 +616,7 @@ export declare const accountTokenResponseSchema: import("./core.js").CompatibleR
613
616
  "audit:read": "audit:read";
614
617
  "content:read": "content:read";
615
618
  "content:write": "content:write";
619
+ "feedback:write": "feedback:write";
616
620
  "page:read": "page:read";
617
621
  "page:write": "page:write";
618
622
  "research:read": "research:read";
@@ -695,6 +699,7 @@ export declare const accountTokenOperation: {
695
699
  "audit:read": "audit:read";
696
700
  "content:read": "content:read";
697
701
  "content:write": "content:write";
702
+ "feedback:write": "feedback:write";
698
703
  "page:read": "page:read";
699
704
  "page:write": "page:write";
700
705
  "research:read": "research:read";
@@ -750,6 +755,7 @@ export declare const accountTokenOperation: {
750
755
  "audit:read": "audit:read";
751
756
  "content:read": "content:read";
752
757
  "content:write": "content:write";
758
+ "feedback:write": "feedback:write";
753
759
  "page:read": "page:read";
754
760
  "page:write": "page:write";
755
761
  "research:read": "research:read";
@@ -897,6 +903,190 @@ export declare const accountTokenOperation: {
897
903
  }];
898
904
  };
899
905
  };
906
+ export declare const accountFeedbackBodySchema: z.ZodObject<{
907
+ command: z.ZodString;
908
+ comment: z.ZodString;
909
+ agent: z.ZodString;
910
+ cliVersion: z.ZodString;
911
+ intent: z.ZodDefault<z.ZodEnum<{
912
+ bug: "bug";
913
+ improvement: "improvement";
914
+ }>>;
915
+ requestId: z.ZodOptional<z.ZodString>;
916
+ }, z.core.$strict>;
917
+ export declare const accountFeedbackResponseSchema: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
918
+ data: z.ZodObject<{
919
+ readonly id: z.ZodString;
920
+ readonly status: z.ZodLiteral<"new">;
921
+ }, z.core.$strip>;
922
+ meta: z.ZodObject<{
923
+ requestId: z.ZodString;
924
+ version: z.ZodLiteral<"1.0">;
925
+ }, z.core.$strict>;
926
+ }, z.core.$strip>, z.ZodObject<{
927
+ data: z.ZodObject<{
928
+ readonly id: z.ZodString;
929
+ readonly status: z.ZodLiteral<"new">;
930
+ }, z.core.$strip>;
931
+ meta: z.ZodObject<{
932
+ requestId: z.ZodString;
933
+ version: z.ZodLiteral<"1.0">;
934
+ }, z.core.$loose>;
935
+ }, z.core.$strip>>;
936
+ export declare const accountFeedbackOperation: {
937
+ readonly id: 'account.feedback';
938
+ readonly method: 'POST';
939
+ readonly path: '/api/v1/account/feedback';
940
+ readonly visibility: 'public';
941
+ readonly semantics: {
942
+ readonly kind: 'mutation';
943
+ readonly sideEffects: 'state';
944
+ readonly retry: 'never';
945
+ readonly readConsistency: null;
946
+ };
947
+ readonly auth: {
948
+ readonly credentials: readonly ["session", "api_token"];
949
+ readonly scopes: readonly ["feedback:write"];
950
+ readonly minimumRole: 'viewer';
951
+ readonly ownership: 'team';
952
+ };
953
+ readonly request: {
954
+ readonly params: null;
955
+ readonly query: null;
956
+ readonly headers: null;
957
+ readonly body: z.ZodObject<{
958
+ command: z.ZodString;
959
+ comment: z.ZodString;
960
+ agent: z.ZodString;
961
+ cliVersion: z.ZodString;
962
+ intent: z.ZodDefault<z.ZodEnum<{
963
+ bug: "bug";
964
+ improvement: "improvement";
965
+ }>>;
966
+ requestId: z.ZodOptional<z.ZodString>;
967
+ }, z.core.$strict>;
968
+ };
969
+ readonly responses: {
970
+ readonly 200: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
971
+ data: z.ZodObject<{
972
+ readonly id: z.ZodString;
973
+ readonly status: z.ZodLiteral<"new">;
974
+ }, z.core.$strip>;
975
+ meta: z.ZodObject<{
976
+ requestId: z.ZodString;
977
+ version: z.ZodLiteral<"1.0">;
978
+ }, z.core.$strict>;
979
+ }, z.core.$strip>, z.ZodObject<{
980
+ data: z.ZodObject<{
981
+ readonly id: z.ZodString;
982
+ readonly status: z.ZodLiteral<"new">;
983
+ }, z.core.$strip>;
984
+ meta: z.ZodObject<{
985
+ requestId: z.ZodString;
986
+ version: z.ZodLiteral<"1.0">;
987
+ }, z.core.$loose>;
988
+ }, z.core.$strip>>;
989
+ };
990
+ readonly errors: readonly ["invalid_request", "unauthorized", "auth_expired", "forbidden", "rate_limited", "contract_violation", "internal_error"];
991
+ readonly errorResponse: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
992
+ readonly error: z.ZodObject<{
993
+ code: z.ZodEnum<{
994
+ auth_expired: "auth_expired";
995
+ conflict: "conflict";
996
+ contract_violation: "contract_violation";
997
+ entitlement_required: "entitlement_required";
998
+ forbidden: "forbidden";
999
+ internal_error: "internal_error";
1000
+ invalid_request: "invalid_request";
1001
+ not_found: "not_found";
1002
+ provider_unavailable: "provider_unavailable";
1003
+ quota_exhausted: "quota_exhausted";
1004
+ rate_limited: "rate_limited";
1005
+ site_ambiguous: "site_ambiguous";
1006
+ stale_evidence: "stale_evidence";
1007
+ unauthorized: "unauthorized";
1008
+ }>;
1009
+ message: z.ZodString;
1010
+ requestId: z.ZodString;
1011
+ retryable: z.ZodBoolean;
1012
+ details: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1013
+ }, z.core.$strict>;
1014
+ }, z.core.$strip>, z.ZodObject<{
1015
+ readonly error: z.ZodObject<{
1016
+ code: z.ZodEnum<{
1017
+ auth_expired: "auth_expired";
1018
+ conflict: "conflict";
1019
+ contract_violation: "contract_violation";
1020
+ entitlement_required: "entitlement_required";
1021
+ forbidden: "forbidden";
1022
+ internal_error: "internal_error";
1023
+ invalid_request: "invalid_request";
1024
+ not_found: "not_found";
1025
+ provider_unavailable: "provider_unavailable";
1026
+ quota_exhausted: "quota_exhausted";
1027
+ rate_limited: "rate_limited";
1028
+ site_ambiguous: "site_ambiguous";
1029
+ stale_evidence: "stale_evidence";
1030
+ unauthorized: "unauthorized";
1031
+ }>;
1032
+ message: z.ZodString;
1033
+ requestId: z.ZodString;
1034
+ retryable: z.ZodBoolean;
1035
+ details: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1036
+ }, z.core.$loose>;
1037
+ }, z.core.$strip>>;
1038
+ readonly resources: {
1039
+ readonly reads: readonly [];
1040
+ readonly changes: readonly [{
1041
+ readonly type: 'account.feedback';
1042
+ readonly idFrom: 'team.id';
1043
+ }];
1044
+ };
1045
+ readonly policy: {
1046
+ readonly rate: {
1047
+ readonly name: 'cli-feedback';
1048
+ readonly limit: 10;
1049
+ readonly windowSeconds: 3600;
1050
+ };
1051
+ readonly idempotency: {
1052
+ readonly _tag: 'none';
1053
+ };
1054
+ readonly entitlement: null;
1055
+ readonly meter: null;
1056
+ };
1057
+ readonly lifecycle: {
1058
+ readonly introduced: '1.0.0';
1059
+ };
1060
+ readonly docs: {
1061
+ readonly summary: 'Submit CLI feedback';
1062
+ readonly description: 'Stores an agent report in the existing feedback queue. Operators track fixes there and review unresolved reports during health checks. No Site or paid subscription is required. Send sanitized reproduction details only. This operation does not retry.';
1063
+ readonly tags: readonly ["Account"];
1064
+ readonly examples: readonly [{
1065
+ readonly request: {
1066
+ readonly body: {
1067
+ readonly command: 'search cohorts';
1068
+ readonly comment: 'Agent report by Codex. Expected a reason for missing data. Received an empty list.';
1069
+ readonly agent: 'Codex';
1070
+ readonly cliVersion: '0.2.1';
1071
+ readonly intent: 'bug';
1072
+ };
1073
+ };
1074
+ readonly response: {
1075
+ readonly status: 200;
1076
+ readonly body: {
1077
+ readonly data: {
1078
+ readonly id: 'fb_01JXYZ';
1079
+ readonly status: 'new';
1080
+ };
1081
+ readonly meta: {
1082
+ readonly requestId: 'req_01JXYZ';
1083
+ readonly version: '1.0';
1084
+ };
1085
+ };
1086
+ };
1087
+ }];
1088
+ };
1089
+ };
900
1090
  export declare const accountV1Registry: import("./core.js").PublicV1Registry<{
901
1091
  readonly usage: {
902
1092
  readonly id: 'account.usage';
@@ -1158,6 +1348,7 @@ export declare const accountV1Registry: import("./core.js").PublicV1Registry<{
1158
1348
  "audit:read": "audit:read";
1159
1349
  "content:read": "content:read";
1160
1350
  "content:write": "content:write";
1351
+ "feedback:write": "feedback:write";
1161
1352
  "page:read": "page:read";
1162
1353
  "page:write": "page:write";
1163
1354
  "research:read": "research:read";
@@ -1213,6 +1404,7 @@ export declare const accountV1Registry: import("./core.js").PublicV1Registry<{
1213
1404
  "audit:read": "audit:read";
1214
1405
  "content:read": "content:read";
1215
1406
  "content:write": "content:write";
1407
+ "feedback:write": "feedback:write";
1216
1408
  "page:read": "page:read";
1217
1409
  "page:write": "page:write";
1218
1410
  "research:read": "research:read";
@@ -1360,4 +1552,158 @@ export declare const accountV1Registry: import("./core.js").PublicV1Registry<{
1360
1552
  }];
1361
1553
  };
1362
1554
  };
1555
+ readonly feedback: {
1556
+ readonly id: 'account.feedback';
1557
+ readonly method: 'POST';
1558
+ readonly path: '/api/v1/account/feedback';
1559
+ readonly visibility: 'public';
1560
+ readonly semantics: {
1561
+ readonly kind: 'mutation';
1562
+ readonly sideEffects: 'state';
1563
+ readonly retry: 'never';
1564
+ readonly readConsistency: null;
1565
+ };
1566
+ readonly auth: {
1567
+ readonly credentials: readonly ["session", "api_token"];
1568
+ readonly scopes: readonly ["feedback:write"];
1569
+ readonly minimumRole: 'viewer';
1570
+ readonly ownership: 'team';
1571
+ };
1572
+ readonly request: {
1573
+ readonly params: null;
1574
+ readonly query: null;
1575
+ readonly headers: null;
1576
+ readonly body: z.ZodObject<{
1577
+ command: z.ZodString;
1578
+ comment: z.ZodString;
1579
+ agent: z.ZodString;
1580
+ cliVersion: z.ZodString;
1581
+ intent: z.ZodDefault<z.ZodEnum<{
1582
+ bug: "bug";
1583
+ improvement: "improvement";
1584
+ }>>;
1585
+ requestId: z.ZodOptional<z.ZodString>;
1586
+ }, z.core.$strict>;
1587
+ };
1588
+ readonly responses: {
1589
+ readonly 200: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
1590
+ data: z.ZodObject<{
1591
+ readonly id: z.ZodString;
1592
+ readonly status: z.ZodLiteral<"new">;
1593
+ }, z.core.$strip>;
1594
+ meta: z.ZodObject<{
1595
+ requestId: z.ZodString;
1596
+ version: z.ZodLiteral<"1.0">;
1597
+ }, z.core.$strict>;
1598
+ }, z.core.$strip>, z.ZodObject<{
1599
+ data: z.ZodObject<{
1600
+ readonly id: z.ZodString;
1601
+ readonly status: z.ZodLiteral<"new">;
1602
+ }, z.core.$strip>;
1603
+ meta: z.ZodObject<{
1604
+ requestId: z.ZodString;
1605
+ version: z.ZodLiteral<"1.0">;
1606
+ }, z.core.$loose>;
1607
+ }, z.core.$strip>>;
1608
+ };
1609
+ readonly errors: readonly ["invalid_request", "unauthorized", "auth_expired", "forbidden", "rate_limited", "contract_violation", "internal_error"];
1610
+ readonly errorResponse: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
1611
+ readonly error: z.ZodObject<{
1612
+ code: z.ZodEnum<{
1613
+ auth_expired: "auth_expired";
1614
+ conflict: "conflict";
1615
+ contract_violation: "contract_violation";
1616
+ entitlement_required: "entitlement_required";
1617
+ forbidden: "forbidden";
1618
+ internal_error: "internal_error";
1619
+ invalid_request: "invalid_request";
1620
+ not_found: "not_found";
1621
+ provider_unavailable: "provider_unavailable";
1622
+ quota_exhausted: "quota_exhausted";
1623
+ rate_limited: "rate_limited";
1624
+ site_ambiguous: "site_ambiguous";
1625
+ stale_evidence: "stale_evidence";
1626
+ unauthorized: "unauthorized";
1627
+ }>;
1628
+ message: z.ZodString;
1629
+ requestId: z.ZodString;
1630
+ retryable: z.ZodBoolean;
1631
+ details: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1632
+ }, z.core.$strict>;
1633
+ }, z.core.$strip>, z.ZodObject<{
1634
+ readonly error: z.ZodObject<{
1635
+ code: z.ZodEnum<{
1636
+ auth_expired: "auth_expired";
1637
+ conflict: "conflict";
1638
+ contract_violation: "contract_violation";
1639
+ entitlement_required: "entitlement_required";
1640
+ forbidden: "forbidden";
1641
+ internal_error: "internal_error";
1642
+ invalid_request: "invalid_request";
1643
+ not_found: "not_found";
1644
+ provider_unavailable: "provider_unavailable";
1645
+ quota_exhausted: "quota_exhausted";
1646
+ rate_limited: "rate_limited";
1647
+ site_ambiguous: "site_ambiguous";
1648
+ stale_evidence: "stale_evidence";
1649
+ unauthorized: "unauthorized";
1650
+ }>;
1651
+ message: z.ZodString;
1652
+ requestId: z.ZodString;
1653
+ retryable: z.ZodBoolean;
1654
+ details: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1655
+ }, z.core.$loose>;
1656
+ }, z.core.$strip>>;
1657
+ readonly resources: {
1658
+ readonly reads: readonly [];
1659
+ readonly changes: readonly [{
1660
+ readonly type: 'account.feedback';
1661
+ readonly idFrom: 'team.id';
1662
+ }];
1663
+ };
1664
+ readonly policy: {
1665
+ readonly rate: {
1666
+ readonly name: 'cli-feedback';
1667
+ readonly limit: 10;
1668
+ readonly windowSeconds: 3600;
1669
+ };
1670
+ readonly idempotency: {
1671
+ readonly _tag: 'none';
1672
+ };
1673
+ readonly entitlement: null;
1674
+ readonly meter: null;
1675
+ };
1676
+ readonly lifecycle: {
1677
+ readonly introduced: '1.0.0';
1678
+ };
1679
+ readonly docs: {
1680
+ readonly summary: 'Submit CLI feedback';
1681
+ readonly description: 'Stores an agent report in the existing feedback queue. Operators track fixes there and review unresolved reports during health checks. No Site or paid subscription is required. Send sanitized reproduction details only. This operation does not retry.';
1682
+ readonly tags: readonly ["Account"];
1683
+ readonly examples: readonly [{
1684
+ readonly request: {
1685
+ readonly body: {
1686
+ readonly command: 'search cohorts';
1687
+ readonly comment: 'Agent report by Codex. Expected a reason for missing data. Received an empty list.';
1688
+ readonly agent: 'Codex';
1689
+ readonly cliVersion: '0.2.1';
1690
+ readonly intent: 'bug';
1691
+ };
1692
+ };
1693
+ readonly response: {
1694
+ readonly status: 200;
1695
+ readonly body: {
1696
+ readonly data: {
1697
+ readonly id: 'fb_01JXYZ';
1698
+ readonly status: 'new';
1699
+ };
1700
+ readonly meta: {
1701
+ readonly requestId: 'req_01JXYZ';
1702
+ readonly version: '1.0';
1703
+ };
1704
+ };
1705
+ };
1706
+ }];
1707
+ };
1708
+ };
1363
1709
  }>;
@@ -233,10 +233,69 @@ export const accountTokenOperation = defineOperation({
233
233
  }],
234
234
  },
235
235
  });
236
+ export const accountFeedbackBodySchema = schema.strictObject({
237
+ command: schema.string().trim().min(1).max(200),
238
+ comment: schema.string().trim().min(1).max(2000),
239
+ agent: schema.string().trim().min(1).max(100),
240
+ cliVersion: schema.string().trim().min(1).max(64),
241
+ intent: schema.enum(['bug', 'improvement']).default('bug'),
242
+ requestId: schema.string().trim().min(1).max(128).optional(),
243
+ });
244
+ export const accountFeedbackResponseSchema = defineSuccessEnvelope(defineResponseObject({
245
+ id: schema.string().min(1),
246
+ status: schema.literal('new'),
247
+ }));
248
+ export const accountFeedbackOperation = defineOperation({
249
+ id: 'account.feedback',
250
+ method: 'POST',
251
+ path: '/api/v1/account/feedback',
252
+ visibility: 'public',
253
+ semantics: { kind: 'mutation', sideEffects: 'state', retry: 'never', readConsistency: null },
254
+ auth: {
255
+ credentials: ['session', 'api_token'],
256
+ scopes: ['feedback:write'],
257
+ minimumRole: 'viewer',
258
+ ownership: 'team',
259
+ },
260
+ request: { params: null, query: null, headers: null, body: accountFeedbackBodySchema },
261
+ responses: { 200: accountFeedbackResponseSchema },
262
+ errors: ['invalid_request', 'unauthorized', 'auth_expired', 'forbidden', 'rate_limited', 'contract_violation', 'internal_error'],
263
+ errorResponse: publicV1ErrorEnvelope,
264
+ resources: {
265
+ reads: [],
266
+ changes: [{ type: 'account.feedback', idFrom: 'team.id' }],
267
+ },
268
+ policy: {
269
+ rate: { name: 'cli-feedback', limit: 10, windowSeconds: 3600 },
270
+ idempotency: { _tag: 'none' },
271
+ entitlement: null,
272
+ meter: null,
273
+ },
274
+ lifecycle: { introduced: '1.0.0' },
275
+ docs: {
276
+ summary: 'Submit CLI feedback',
277
+ description: 'Stores an agent report in the existing feedback queue. Operators track fixes there and review unresolved reports during health checks. No Site or paid subscription is required. Send sanitized reproduction details only. This operation does not retry.',
278
+ tags: ['Account'],
279
+ examples: [{
280
+ request: { body: {
281
+ command: 'search cohorts',
282
+ comment: 'Agent report by Codex. Expected a reason for missing data. Received an empty list.',
283
+ agent: 'Codex',
284
+ cliVersion: '0.2.1',
285
+ intent: 'bug',
286
+ } },
287
+ response: { status: 200, body: {
288
+ data: { id: 'fb_01JXYZ', status: 'new' },
289
+ meta: { requestId: 'req_01JXYZ', version: '1.0' },
290
+ } },
291
+ }],
292
+ },
293
+ });
236
294
  export const accountV1Registry = defineRegistry({
237
295
  feature: 'account',
238
296
  operations: {
239
297
  usage: accountUsageOperation,
240
298
  token: accountTokenOperation,
299
+ feedback: accountFeedbackOperation,
241
300
  },
242
301
  });
package/dist/v1/core.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare const publicV1StandardRatePolicy: {
20
20
  export declare const PUBLIC_V1_FEATURES: readonly ['account', 'actions', 'analytics', 'audit', 'backlinks', 'content', 'mentions', 'page', 'research', 'sites', 'timeline'];
21
21
  export declare const PUBLIC_V1_METHODS: readonly ['DELETE', 'GET', 'PATCH', 'POST'];
22
22
  export declare const PUBLIC_V1_CREDENTIALS: readonly ['api_token', 'session'];
23
- export declare const PUBLIC_V1_SCOPES: readonly ['account:read', 'actions:read', 'actions:write', 'analytics:read', 'audit:read', 'content:read', 'content:write', 'page:read', 'page:write', 'research:read', 'research:write', 'sites:read', 'sites:write', 'timeline:read', 'timeline:write'];
23
+ export declare const PUBLIC_V1_SCOPES: readonly ['account:read', 'actions:read', 'actions:write', 'analytics:read', 'audit:read', 'content:read', 'content:write', 'feedback:write', 'page:read', 'page:write', 'research:read', 'research:write', 'sites:read', 'sites:write', 'timeline:read', 'timeline:write'];
24
24
  export declare const PUBLIC_V1_ROLES: readonly ['viewer', 'member', 'admin', 'owner'];
25
25
  export declare const PUBLIC_V1_ERROR_CODES: readonly ['invalid_request', 'unauthorized', 'forbidden', 'not_found', 'site_ambiguous', 'auth_expired', 'entitlement_required', 'quota_exhausted', 'provider_unavailable', 'stale_evidence', 'conflict', 'rate_limited', 'contract_violation', 'internal_error'];
26
26
  export declare const PUBLIC_V1_ERROR_STATUS: {
@@ -47,7 +47,7 @@ export type PublicV1Role = typeof PUBLIC_V1_ROLES[number];
47
47
  export type PublicV1ErrorCode = typeof PUBLIC_V1_ERROR_CODES[number];
48
48
  export type PublicV1Visibility = 'internal' | 'public';
49
49
  export type PublicV1OwnershipRule = 'principal' | 'site' | 'team';
50
- export type PublicV1ResourceType = 'account.usage' | 'site.actions' | 'site.analytics' | 'site.audit' | 'site.collection' | 'site.content' | 'site.identity' | 'site.page' | 'site.research' | 'site.sitemaps' | 'site.timeline';
50
+ export type PublicV1ResourceType = 'account.usage' | 'account.feedback' | 'site.actions' | 'site.analytics' | 'site.audit' | 'site.collection' | 'site.content' | 'site.identity' | 'site.page' | 'site.research' | 'site.sitemaps' | 'site.timeline';
51
51
  export interface PublicV1ExpectedFailure {
52
52
  _tag: 'ApiFailure';
53
53
  code: PublicV1ErrorCode;
package/dist/v1/core.js CHANGED
@@ -42,6 +42,7 @@ export const PUBLIC_V1_SCOPES = [
42
42
  'audit:read',
43
43
  'content:read',
44
44
  'content:write',
45
+ 'feedback:write',
45
46
  'page:read',
46
47
  'page:write',
47
48
  'research:read',