evolcore 0.0.3 → 0.0.4

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 (143) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +45 -10
  3. package/bin/ec-safe-output.js +89 -24
  4. package/dist/agents/baseagent.js +46 -0
  5. package/dist/agents/claude-runner.js +1 -31
  6. package/dist/agents/codex-app-server-client.js +68 -0
  7. package/dist/agents/codex-runner.js +157 -5
  8. package/dist/agents/runner-types.js +2 -2
  9. package/dist/aun/aid/agentmd.js +79 -0
  10. package/dist/aun/aid/encryption-seed-policy.js +29 -0
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/aid/store.js +2 -5
  13. package/dist/channels/aun.js +220 -112
  14. package/dist/channels/daemon.js +18 -4
  15. package/dist/channels/dingtalk.js +52 -137
  16. package/dist/channels/feishu.js +56 -4
  17. package/dist/channels/qqbot.js +23 -1
  18. package/dist/channels/wechat.js +303 -155
  19. package/dist/channels/wecom.js +383 -7
  20. package/dist/cli/aun-commands.js +11 -11
  21. package/dist/cli/handoff-command.js +2 -1
  22. package/dist/cli/init-channel.js +185 -67
  23. package/dist/cli/init.js +7 -5
  24. package/dist/cli/trigger-command.js +92 -11
  25. package/dist/config/access-policy-domain.js +38 -0
  26. package/dist/config/access-policy.js +134 -0
  27. package/dist/config/builtin-role-templates.js +27 -14
  28. package/dist/config/builtin-roles.js +25 -6
  29. package/dist/config/config-field-policy.js +17 -5
  30. package/dist/config/config-manager.js +198 -22
  31. package/dist/config/config-operation-service.js +35 -38
  32. package/dist/config/contact-bind-code.js +274 -0
  33. package/dist/config/contact-book-store.js +173 -5
  34. package/dist/config/contact-book.js +32 -0
  35. package/dist/config/contact-operation-service.js +9 -3
  36. package/dist/config/contact-request-service.js +331 -0
  37. package/dist/config/peer-role-resolver.js +3 -1
  38. package/dist/config/schema-registry.js +49 -24
  39. package/dist/config-store.js +14 -2
  40. package/dist/core/auth/agent-delegation.js +105 -11
  41. package/dist/core/auth/authorization-audit.js +6 -0
  42. package/dist/core/auth/operation-authorizer.js +8 -0
  43. package/dist/core/auth/operation-catalog.js +51 -3
  44. package/dist/core/auth/trigger-authorization.js +15 -0
  45. package/dist/core/bootstrap-service.js +2 -9
  46. package/dist/core/channel-loader.js +6 -2
  47. package/dist/core/command/command-handler.js +10 -13
  48. package/dist/core/command/connect-menu.js +249 -24
  49. package/dist/core/command/evol-menu-version-gate.js +38 -0
  50. package/dist/core/command/group-menu.js +421 -0
  51. package/dist/core/command/menu-handler.js +269 -85
  52. package/dist/core/command/menu-protocol.js +8 -2
  53. package/dist/core/command/menu-token-store.js +102 -0
  54. package/dist/core/command/slash-gate.js +1 -1
  55. package/dist/core/command/slash-handler.js +158 -31
  56. package/dist/core/daemon-file-cache.js +28 -0
  57. package/dist/core/event-catalog.js +29 -0
  58. package/dist/core/evolagent-registry.js +4 -39
  59. package/dist/core/handoff/runtime.js +162 -37
  60. package/dist/core/handoff/store.js +46 -2
  61. package/dist/core/handoff/types.js +1 -0
  62. package/dist/core/inference/text-inference.js +16 -74
  63. package/dist/core/message/file-markers.js +7 -0
  64. package/dist/core/message/im-renderer.js +18 -14
  65. package/dist/core/message/inbound-admission.js +124 -0
  66. package/dist/core/message/message-bridge.js +616 -74
  67. package/dist/core/message/message-log.js +1 -0
  68. package/dist/core/message/message-queue.js +185 -10
  69. package/dist/core/message/peer-mode.js +7 -8
  70. package/dist/core/message/response-engine.js +381 -79
  71. package/dist/core/permission/approval-gateway.js +17 -10
  72. package/dist/core/permission/ec-command-parser.js +101 -46
  73. package/dist/core/permission/tool-policy.js +69 -24
  74. package/dist/core/protected-paths.js +36 -11
  75. package/dist/core/session/session-manager.js +25 -3
  76. package/dist/core/session/session-mapper.js +2 -0
  77. package/dist/core/session/session-renew.js +125 -69
  78. package/dist/core/session/session-turn-coordinator.js +5 -1
  79. package/dist/eck/kit-renderer.js +12 -1
  80. package/dist/eck/message-renderer.js +79 -1
  81. package/dist/index.js +224 -89
  82. package/dist/ipc.js +81 -4
  83. package/dist/paths.js +3 -0
  84. package/dist/response-system/config-resolver.js +29 -0
  85. package/dist/response-system/coordinator.js +8 -32
  86. package/dist/response-system/engines/v1/proactive-flow.js +1 -1
  87. package/dist/response-system/index.js +1 -0
  88. package/dist/response-system/modes/single-session/index.js +7 -4
  89. package/dist/trigger/parser.js +55 -15
  90. package/dist/trigger/patch.js +4 -1
  91. package/dist/trigger/scheduler.js +441 -39
  92. package/dist/utils/error-dict.json +7 -0
  93. package/dist/utils/evolcore-version.js +21 -0
  94. package/dist/utils/logger.js +2 -2
  95. package/dist/utils/stable-semver.js +21 -0
  96. package/dist/utils/stats.js +33 -9
  97. package/kits/docs/channels/aun.md +4 -13
  98. package/kits/docs/evolcore/INDEX.md +1 -1
  99. package/kits/docs/evolcore/config.md +47 -2
  100. package/kits/docs/evolcore/contact.md +7 -3
  101. package/kits/docs/evolcore/group-rules.md +46 -4
  102. package/kits/docs/evolcore/group.md +4 -4
  103. package/kits/docs/evolcore/msg.md +5 -5
  104. package/kits/docs/evolcore/trigger.md +25 -8
  105. package/kits/docs/path-registry.md +36 -17
  106. package/kits/eck_message_manifest.json +12 -1
  107. package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
  108. package/kits/rules/01-overview.md +17 -7
  109. package/kits/rules/02-navigation.md +38 -18
  110. package/kits/rules/03-identity.md +28 -24
  111. package/kits/rules/04-relation.md +44 -28
  112. package/kits/rules/05-venue.md +31 -15
  113. package/kits/rules/06-channel.md +11 -7
  114. package/kits/schemas/_meta.json +18 -7
  115. package/kits/schemas/agent-config.schema.7.json +303 -0
  116. package/kits/schemas/agent-config.schema.8.json +304 -0
  117. package/kits/schemas/agent-config.schema.9.json +374 -0
  118. package/kits/schemas/contact-book.schema.3.json +67 -0
  119. package/kits/schemas/daemon.schema.1.json +3 -2
  120. package/kits/schemas/daemon.schema.2.json +101 -0
  121. package/kits/schemas/daemon.schema.3.json +123 -0
  122. package/kits/schemas/defaults.schema.2.json +85 -0
  123. package/kits/schemas/defaults.schema.3.json +73 -0
  124. package/kits/schemas/relation-config.schema.6.json +46 -0
  125. package/kits/schemas/relation-config.schema.7.json +59 -0
  126. package/kits/schemas/single-session.schema.2.json +57 -0
  127. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
  128. package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
  129. package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
  130. package/kits/templates/roles/admin.json +46 -0
  131. package/kits/templates/roles/member.json +4 -0
  132. package/kits/templates/roles/visitor.json +4 -0
  133. package/kits/templates/system-fragments/channel.md +12 -2
  134. package/kits/templates/system-fragments/identity.md +3 -1
  135. package/kits/templates/system-fragments/relation.md +1 -1
  136. package/kits/templates/system-fragments/session.md +6 -2
  137. package/package.json +4 -2
  138. package/MIGRATION-0.5.0.md +0 -378
  139. package/ROLE_ACCESS_CONTROL.md +0 -174
  140. package/dist/channels/contact-bind-code.js +0 -134
  141. package/dist/channels/wecom-card.js +0 -101
  142. package/dist/channels/wecom-onboarding.js +0 -82
  143. package/dist/channels/wecom-state.js +0 -191
@@ -0,0 +1,38 @@
1
+ import { isValidAid } from '../aun/aid/validation.js';
2
+ export function validAgentOwnerIds(agent) {
3
+ return [...new Set((agent?.owners ?? [])
4
+ .map(value => String(value || '').trim())
5
+ .filter(isValidAid))];
6
+ }
7
+ export function configuredChannelKeys(selfAid, agent) {
8
+ const keys = (agent?.channels ?? [])
9
+ .filter(channel => channel && channel.enabled !== false)
10
+ .map(channel => {
11
+ const type = String(channel.type || '').trim().toLowerCase();
12
+ const name = String(channel.name || '').trim();
13
+ return type && name ? `${type}#${selfAid}#${name}` : '';
14
+ })
15
+ .filter(Boolean);
16
+ keys.push(`aun#${selfAid}#main`);
17
+ return [...new Set(keys)];
18
+ }
19
+ export function validateAccessPolicyDomain(selfAid, agent) {
20
+ const access = agent.access;
21
+ if (!access)
22
+ return undefined;
23
+ if (access.policyMode !== 'open' && validAgentOwnerIds(agent).length === 0) {
24
+ return {
25
+ code: 'restricted_mode_without_owner',
26
+ message: `Agent ${selfAid} cannot use ${access.policyMode} without an Owner`,
27
+ };
28
+ }
29
+ const configured = new Set(configuredChannelKeys(selfAid, agent));
30
+ const unknown = access.allowedGroupChannels.find(channelKey => !configured.has(channelKey));
31
+ if (unknown) {
32
+ return {
33
+ code: 'unknown_group_access_channel',
34
+ message: `Group access channel is not configured or enabled for ${selfAid}: ${unknown}`,
35
+ };
36
+ }
37
+ return undefined;
38
+ }
@@ -0,0 +1,134 @@
1
+ import crypto from 'crypto';
2
+ import { ConfigError, ConfigTarget, mutateConfig, read, validateConfig, } from './config-manager.js';
3
+ import { configuredChannelKeys, validAgentOwnerIds, validateAccessPolicyDomain, } from './access-policy-domain.js';
4
+ export class AccessPolicyError extends Error {
5
+ code;
6
+ data;
7
+ constructor(code, message, data) {
8
+ super(message);
9
+ this.code = code;
10
+ this.data = data;
11
+ this.name = 'AccessPolicyError';
12
+ }
13
+ }
14
+ export const DEFAULT_ACCESS_POLICY = Object.freeze({
15
+ policyMode: 'open',
16
+ allowedGroupChannels: Object.freeze([]),
17
+ requestLimit: Object.freeze({ maxTimes: 600, coolDown: 60 * 60 }),
18
+ });
19
+ export function listAgentOwners(selfAid) {
20
+ const agent = read(ConfigTarget.Agent, { self: selfAid }, { cache: true });
21
+ return validAgentOwnerIds(agent);
22
+ }
23
+ export function listConfiguredChannelKeys(selfAid) {
24
+ const agent = read(ConfigTarget.Agent, { self: selfAid }, { cache: true });
25
+ if (!agent)
26
+ return [];
27
+ return configuredChannelKeys(selfAid, agent);
28
+ }
29
+ /** Missing AgentConfig.access uses the compatibility view; malformed Agent config never does. */
30
+ export function readAccessPolicy(selfAid) {
31
+ return readAccessPolicySnapshot(selfAid).policy;
32
+ }
33
+ export function readAccessPolicySnapshot(selfAid) {
34
+ const agent = read(ConfigTarget.Agent, { self: selfAid }, { cache: true });
35
+ return accessPolicySnapshotFromAgent(selfAid, agent);
36
+ }
37
+ /**
38
+ * Atomically updates AgentConfig.access through Config Manager's file-scoped
39
+ * mutation path. The callback always receives the newest persisted policy.
40
+ */
41
+ export function mutateAccessPolicy(input) {
42
+ try {
43
+ const result = mutateConfig(ConfigTarget.Agent, { self: input.selfAid }, agent => {
44
+ const current = accessPolicySnapshotFromAgent(input.selfAid, agent);
45
+ if (input.expectedAccessRevision !== current.accessRevision) {
46
+ throw new AccessPolicyError('access_policy_revision_conflict', 'Access policy changed since it was read', {
47
+ expectedAccessRevision: input.expectedAccessRevision,
48
+ accessRevision: current.accessRevision,
49
+ });
50
+ }
51
+ const next = input.update(clonePolicy(current.policy));
52
+ assertAccessPolicyWrite(input, agent, current.policy, next);
53
+ agent.access = clonePolicy(next);
54
+ return {
55
+ policy: clonePolicy(next),
56
+ accessRevision: accessPolicyRevision(next),
57
+ };
58
+ });
59
+ return { ...result.value, changed: result.changed };
60
+ }
61
+ catch (error) {
62
+ if (error instanceof AccessPolicyError)
63
+ throw error;
64
+ if (error instanceof ConfigError && error.code === 'LOCK_TIMEOUT') {
65
+ throw new AccessPolicyError('access_policy_temporarily_unavailable', 'Access policy is temporarily unavailable');
66
+ }
67
+ if (error instanceof ConfigError) {
68
+ throw new AccessPolicyError('invalid_access_policy', error.message);
69
+ }
70
+ throw error;
71
+ }
72
+ }
73
+ function accessPolicySnapshotFromAgent(selfAid, agent) {
74
+ if (!agent) {
75
+ throw new AccessPolicyError('invalid_access_policy', `Agent config does not exist: ${selfAid}`);
76
+ }
77
+ const errors = validateConfig(ConfigTarget.Agent, agent);
78
+ if (errors.length > 0) {
79
+ throw new AccessPolicyError('invalid_access_policy', `Invalid access policy for ${selfAid}: ${errors.join('; ')}`);
80
+ }
81
+ const issue = validateAccessPolicyDomain(selfAid, agent);
82
+ if (issue)
83
+ throw new AccessPolicyError(issue.code, issue.message);
84
+ const access = agent.access ?? DEFAULT_ACCESS_POLICY;
85
+ const policy = clonePolicy(access);
86
+ return { policy, accessRevision: accessPolicyRevision(policy) };
87
+ }
88
+ function assertAccessPolicyWrite(input, agent, current, policy) {
89
+ const owners = validAgentOwnerIds(agent);
90
+ const normalizedActorAid = String(input.actorAid || '').trim();
91
+ const isOwner = input.actorRole === 'owner' || owners.includes(normalizedActorAid);
92
+ const isGroupChannelAdmin = input.actorRole === 'admin' && input.resource === 'access.allowedGroupChannels';
93
+ if (!isOwner && !isGroupChannelAdmin) {
94
+ if (input.resource === 'access.allowedGroupChannels') {
95
+ throw new AccessPolicyError('access_policy_manager_required', 'Only an Agent Owner or Admin can change allowed group channels');
96
+ }
97
+ throw new AccessPolicyError('access_policy_owner_required', 'Only an Agent Owner can change access policy');
98
+ }
99
+ if (isGroupChannelAdmin) {
100
+ if (current.policyMode !== policy.policyMode
101
+ || current.requestLimit.maxTimes !== policy.requestLimit.maxTimes
102
+ || current.requestLimit.coolDown !== policy.requestLimit.coolDown) {
103
+ throw new AccessPolicyError('access_policy_owner_required', 'An Agent Admin may only change allowed group channels');
104
+ }
105
+ }
106
+ if (policy.policyMode !== 'open' && owners.length === 0) {
107
+ throw new AccessPolicyError('restricted_mode_without_owner', `Agent ${input.selfAid} cannot use ${policy.policyMode} without an Owner`);
108
+ }
109
+ const configured = new Set(configuredChannelKeys(input.selfAid, agent));
110
+ const unknown = policy.allowedGroupChannels.find(channelKey => !configured.has(channelKey));
111
+ if (unknown) {
112
+ throw new AccessPolicyError('unknown_group_access_channel', `Group access channel is not configured or enabled for ${input.selfAid}: ${unknown}`);
113
+ }
114
+ }
115
+ function clonePolicy(policy) {
116
+ return {
117
+ policyMode: policy.policyMode,
118
+ allowedGroupChannels: [...policy.allowedGroupChannels],
119
+ requestLimit: { ...policy.requestLimit },
120
+ };
121
+ }
122
+ function accessPolicyRevision(policy) {
123
+ return `sha256:${crypto.createHash('sha256').update(stableStringify(policy)).digest('hex')}`;
124
+ }
125
+ function stableStringify(value) {
126
+ return JSON.stringify(sortValue(value));
127
+ }
128
+ function sortValue(value) {
129
+ if (Array.isArray(value))
130
+ return value.map(sortValue);
131
+ if (!value || typeof value !== 'object')
132
+ return value;
133
+ return Object.fromEntries(Object.keys(value).sort().map(key => [key, sortValue(value[key])]));
134
+ }
@@ -1,6 +1,6 @@
1
- import fs from 'fs';
2
1
  import path from 'path';
3
2
  import { kitsRoleTemplatesDir } from '../paths.js';
3
+ import { createErrorCapturingReader, fileCache } from '../core/daemon-file-cache.js';
4
4
  import { validateRoleConfig } from './role-schema.js';
5
5
  import { expectedRoleRank } from './role-ranks.js';
6
6
  export const BUILTIN_ROLE_IDS = ['owner', 'admin', 'member', 'visitor'];
@@ -24,19 +24,32 @@ export function validateBuiltinRoleTemplateDefinition(roleId, raw) {
24
24
  }
25
25
  return { definition: definition };
26
26
  }
27
- /** Load the versioned package template that defines a built-in role policy. */
27
+ /**
28
+ * Load the versioned package template that defines a built-in role policy.
29
+ *
30
+ * 模板随包发布、只靠进程重启或 kit 缓存失效刷新 → fileCache 的 on-reload 策略,
31
+ * 归入 'kits' 组。缓存的是"读盘 + JSON.parse"结果;校验(validateRoleConfig)仍每次现做。
32
+ */
28
33
  export function readBuiltinRoleTemplate(roleId) {
29
34
  const file = path.join(kitsRoleTemplatesDir(), `${roleId}.json`);
30
- let raw;
31
- try {
32
- raw = JSON.parse(fs.readFileSync(file, 'utf8'));
33
- }
34
- catch (error) {
35
- return {
36
- definition: null,
37
- file,
38
- error: `cannot read built-in role template: ${error instanceof Error ? error.message : String(error)}`,
39
- };
40
- }
41
- return { ...validateBuiltinRoleTemplateDefinition(roleId, raw), file };
35
+ // 默认 reader 会把所有读盘异常吞成 null(分不清 ENOENT 与 EACCES),
36
+ // 这里注入带错误捕获的 reader 把真实错误原文带出来,保持与直读时一致的报错。
37
+ const reader = createErrorCapturingReader();
38
+ const parsed = fileCache.get(file, (text) => {
39
+ if (text === null) {
40
+ return { error: `cannot read built-in role template: ${reader.lastError()}` };
41
+ }
42
+ try {
43
+ return { raw: JSON.parse(text) };
44
+ }
45
+ catch (error) {
46
+ return { error: `cannot read built-in role template: ${error instanceof Error ? error.message : String(error)}` };
47
+ }
48
+ }, { policy: 'on-reload', group: 'kits', read: reader.read });
49
+ if ('error' in parsed)
50
+ return { definition: null, file, error: parsed.error };
51
+ // 缓存返回的是同一个对象引用,而 validateBuiltinRoleTemplateDefinition 里的解构
52
+ // 只隔离顶层——permissions / commandPermissions / usageLimits 等嵌套对象仍会被
53
+ // 所有调用方共享,任何一处改写都会污染后续读者拿到的授权策略。整份深拷贝后再校验。
54
+ return { ...validateBuiltinRoleTemplateDefinition(roleId, structuredClone(parsed.raw)), file };
42
55
  }
@@ -21,17 +21,23 @@ export function getBuiltinRolesConfig() {
21
21
  };
22
22
  }
23
23
  /**
24
- * Management grants are a code-level ceiling, not configurable role policy.
25
- * A role file may narrow these rules but can never add a management grant.
24
+ * Management grants are a code-level ceiling. The built-in admin template
25
+ * declares the default grants; local role files may narrow them but can never
26
+ * add a grant outside this ceiling.
26
27
  */
27
28
  export function getManagementCommandPermissions(role) {
28
29
  return {
29
30
  'contact.read': { allow: true, scopes: ['agent'] },
30
- 'contact.block': { allow: true, scopes: ['agent'] },
31
+ 'contact.block': { allow: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
32
+ 'connect.write': { allow: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
33
+ 'connect.access.write': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true } },
31
34
  'role.assign': { allow: true, scopes: ['agent'] },
32
35
  'role.revoke': { allow: true, scopes: ['agent'] },
33
36
  'role.policy.read': { allow: true, scopes: ['agent'] },
34
37
  'role.relation.read': { allow: true, scopes: ['agent'] },
38
+ 'config.get': { allow: true, scopes: ['agent', 'relation'] },
39
+ 'config.set': { allow: true, scopes: ['agent', 'relation'] },
40
+ 'config.unset': { allow: true, scopes: ['agent', 'relation'] },
35
41
  'role.policy.write': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
36
42
  '*': { allow: true },
37
43
  'agent.reload': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
@@ -39,6 +45,7 @@ export function getManagementCommandPermissions(role) {
39
45
  };
40
46
  }
41
47
  export function applyManagementRoleCeiling(role, configured) {
48
+ const builtin = readBuiltinRoleTemplate(role).definition;
42
49
  const fallback = {
43
50
  description: role === 'owner' ? 'Agent owner' : 'Agent administrator',
44
51
  rank: expectedRoleRank(role),
@@ -48,12 +55,16 @@ export function applyManagementRoleCeiling(role, configured) {
48
55
  },
49
56
  commandPermissions: getManagementCommandPermissions(role),
50
57
  };
51
- const policy = configured ?? readBuiltinRoleTemplate(role).definition ?? fallback;
58
+ const policy = configured ?? builtin ?? fallback;
59
+ const configuredCommandPermissions = policy.commandPermissions;
60
+ const defaultCommandPermissions = configuredCommandPermissions
61
+ ?? (role === 'admin' && builtin ? builtin.commandPermissions : undefined)
62
+ ?? getManagementCommandPermissions(role);
52
63
  return {
53
64
  ...policy,
54
65
  rank: expectedRoleRank(role),
55
- allowAccess: true,
56
- commandPermissions: narrowManagementCommandPermissions(getManagementCommandPermissions(role), policy.commandPermissions || {}),
66
+ allowAccess: role === 'owner' ? true : policy.allowAccess !== false,
67
+ commandPermissions: narrowManagementCommandPermissions(getManagementCommandPermissions(role), defaultCommandPermissions),
57
68
  };
58
69
  }
59
70
  export function getManagementRoleDefinition(role) {
@@ -78,6 +89,14 @@ function narrowManagementCommandPermissions(ceiling, configured) {
78
89
  else if (!Array.isArray(permission.scopes) && Array.isArray(override.scopes)) {
79
90
  next.scopes = [...override.scopes];
80
91
  }
92
+ if (permission.constraints || override.constraints) {
93
+ // Configured constraints may add restrictions, but a role file cannot
94
+ // weaken a code-level constraint (the ceiling wins on collisions).
95
+ next.constraints = {
96
+ ...(override.constraints ?? {}),
97
+ ...(permission.constraints ?? {}),
98
+ };
99
+ }
81
100
  merged[operation] = next;
82
101
  }
83
102
  // Exact or namespace denies may narrow grants inherited through the ceiling's
@@ -8,6 +8,7 @@ const BEHAVIOR_TOP_FIELDS = new Set([
8
8
  'debounce',
9
9
  'show_activities',
10
10
  'session_renew',
11
+ 'groupRules',
11
12
  'group_venue_sync',
12
13
  'render',
13
14
  'sessionManifests',
@@ -32,6 +33,7 @@ const SENSITIVE_TOP_FIELDS = new Set([
32
33
  'lifecycle',
33
34
  'initialized',
34
35
  'owners',
36
+ 'access',
35
37
  'admins',
36
38
  'channels',
37
39
  'aun',
@@ -44,10 +46,10 @@ const SENSITIVE_TOP_FIELDS = new Set([
44
46
  'role',
45
47
  'extra_backup',
46
48
  ]);
47
- const CRITICAL_AGENT_FIELDS = new Set(['owners', 'admins', 'roles']);
49
+ const CRITICAL_AGENT_FIELDS = new Set(['owners', 'admins', 'roles', 'access']);
48
50
  const EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
49
- function scalar(rolePermissionKey, valueKind) {
50
- return { class: 'safe-scalar', rolePermissionKey, valueKind };
51
+ function scalar(rolePermissionKey, valueKind, access = 'role-overridable') {
52
+ return { class: 'safe-scalar', access, rolePermissionKey, valueKind };
51
53
  }
52
54
  function readonlyObject(rolePermissionKey) {
53
55
  return { class: 'safe-readonly-object', rolePermissionKey };
@@ -133,8 +135,6 @@ export function resolveConfigFieldRule(fieldPath) {
133
135
  return scalar('session_renew', 'boolean');
134
136
  if (parts[1] === 'after_hours')
135
137
  return scalar('session_renew', 'positive-number');
136
- if (parts[1] === 'effort')
137
- return scalar('session_renew', 'effort');
138
138
  if (parts[1] === 'fallback_action')
139
139
  return scalar('session_renew', 'session-renew-action');
140
140
  return { class: 'unknown' };
@@ -153,6 +153,14 @@ export function resolveConfigFieldRule(fieldPath) {
153
153
  return scalar('sessionManifests', 'manifest-file');
154
154
  return { class: 'unknown' };
155
155
  }
156
+ if (top === 'groupRules') {
157
+ if (parts.length === 1)
158
+ return readonlyObject('groupRules');
159
+ if (field === 'groupRules.mode') {
160
+ return scalar(field, 'group-rules-mode', 'owner-only');
161
+ }
162
+ return { class: 'unknown' };
163
+ }
156
164
  if (top === 'group_venue_sync')
157
165
  return parts.length === 1
158
166
  ? readonlyObject('group_venue_sync')
@@ -257,5 +265,9 @@ export function parseConfigFieldValue(fieldPath, rawValue) {
257
265
  return raw === 'continue' || raw === 'new'
258
266
  ? { ok: true, value: raw }
259
267
  : { ok: false, reason: `Config field ${fieldPath} must be continue or new` };
268
+ case 'group-rules-mode':
269
+ return raw === 'load' || raw === 'ignore'
270
+ ? { ok: true, value: raw }
271
+ : { ok: false, reason: `Config field ${fieldPath} must be load or ignore` };
260
272
  }
261
273
  }