evolcore 0.0.1 → 0.0.3
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.
- package/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/MIGRATION-0.5.0.md +378 -0
- package/README.md +318 -14
- package/ROLE_ACCESS_CONTROL.md +174 -0
- package/assets/.env.template +4 -0
- package/bin/ec-safe-output.js +161 -0
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent.js +163 -0
- package/dist/agents/claude-runner.js +2565 -0
- package/dist/agents/codex-app-server-client.js +448 -0
- package/dist/agents/codex-runner.js +2682 -0
- package/dist/agents/gemini-runner.js +666 -0
- package/dist/agents/runner-types.js +75 -0
- package/dist/aun/aid/agentmd.js +216 -0
- package/dist/aun/aid/client.js +132 -0
- package/dist/aun/aid/control-aid.js +91 -0
- package/dist/aun/aid/identity.js +518 -0
- package/dist/aun/aid/index.js +4 -0
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/group-identity.js +10 -0
- package/dist/aun/msg/group-index.js +6 -0
- package/dist/aun/msg/group.js +1231 -0
- package/dist/aun/msg/history.js +123 -0
- package/dist/aun/msg/index.js +5 -0
- package/dist/aun/msg/p2p.js +393 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +137 -0
- package/dist/aun/outbox.js +168 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +25 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/service-proxy.js +225 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +68 -0
- package/dist/channels/aun.js +4164 -0
- package/dist/channels/contact-bind-code.js +134 -0
- package/dist/channels/daemon.js +422 -0
- package/dist/channels/dingtalk.js +1479 -0
- package/dist/channels/feishu.js +1865 -0
- package/dist/channels/qqbot.js +409 -0
- package/dist/channels/wechat.js +817 -0
- package/dist/channels/wecom-card.js +101 -0
- package/dist/channels/wecom-onboarding.js +82 -0
- package/dist/channels/wecom-state.js +191 -0
- package/dist/channels/wecom.js +1157 -0
- package/dist/cli/agent-command.js +642 -0
- package/dist/cli/agent.js +1059 -0
- package/dist/cli/aun-commands.js +2003 -0
- package/dist/cli/bench.js +1228 -0
- package/dist/cli/cli-argv.js +66 -0
- package/dist/cli/code-stats.js +329 -0
- package/dist/cli/command-log.js +82 -0
- package/dist/cli/config-selector.js +69 -0
- package/dist/cli/config.js +261 -0
- package/dist/cli/contact.js +71 -0
- package/dist/cli/ctl-command.js +62 -0
- package/dist/cli/daemon-commands.js +2750 -0
- package/dist/cli/fs-command.js +1447 -0
- package/dist/cli/handoff-command.js +302 -0
- package/dist/cli/help.js +35 -0
- package/dist/cli/index.js +374 -0
- package/dist/cli/init-channel.js +1372 -0
- package/dist/cli/init.js +590 -0
- package/dist/cli/link-rules.js +240 -0
- package/dist/cli/model.js +591 -0
- package/dist/cli/net-check.js +723 -0
- package/dist/cli/queue-command.js +150 -0
- package/dist/cli/raw-key-input.js +25 -0
- package/dist/cli/response.js +344 -0
- package/dist/cli/restart-monitor.js +480 -0
- package/dist/cli/stats.js +609 -0
- package/dist/cli/task-context.js +80 -0
- package/dist/cli/trigger-command.js +545 -0
- package/dist/cli/version.js +93 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +673 -0
- package/dist/config/boot-log.js +266 -0
- package/dist/config/builtin-role-templates.js +42 -0
- package/dist/config/builtin-roles.js +91 -0
- package/dist/config/config-batch-get.js +11 -0
- package/dist/config/config-field-policy.js +261 -0
- package/dist/config/config-manager.js +1120 -0
- package/dist/config/config-operation-service.js +384 -0
- package/dist/config/contact-alias.js +68 -0
- package/dist/config/contact-book-store.js +454 -0
- package/dist/config/contact-book-v2-startup.js +35 -0
- package/dist/config/contact-book.js +224 -0
- package/dist/config/contact-operation-service.js +110 -0
- package/dist/config/gateway-config.js +858 -0
- package/dist/config/lifecycle.js +17 -0
- package/dist/config/mention-mode.js +27 -0
- package/dist/config/merge.js +161 -0
- package/dist/config/owner-policy.js +4 -0
- package/dist/config/peer-role-resolver.js +218 -0
- package/dist/config/resolved-config-op.js +483 -0
- package/dist/config/role-config-v4-startup.js +32 -0
- package/dist/config/role-config-v5-startup.js +27 -0
- package/dist/config/role-ranks.js +18 -0
- package/dist/config/role-schema.js +105 -0
- package/dist/config/role-service.js +156 -0
- package/dist/config/role-store.js +215 -0
- package/dist/config/roles.js +64 -0
- package/dist/config/schema-registry.js +154 -0
- package/dist/config/snapshot.js +598 -0
- package/dist/config-store.js +501 -0
- package/dist/core/auth/agent-delegation.js +111 -0
- package/dist/core/auth/auth-gateway.js +166 -0
- package/dist/core/auth/authenticated-actor.js +6 -0
- package/dist/core/auth/authorization-audit.js +119 -0
- package/dist/core/auth/operation-authorizer.js +720 -0
- package/dist/core/auth/operation-catalog.js +731 -0
- package/dist/core/baseagent-loader.js +54 -0
- package/dist/core/bootstrap-service.js +175 -0
- package/dist/core/capability/capability-manager.js +316 -0
- package/dist/core/capability/providers/claude-capability-provider.js +176 -0
- package/dist/core/capability/providers/codex-capability-provider.js +148 -0
- package/dist/core/capability/providers/gemini-capability-provider.js +10 -0
- package/dist/core/capability/types.js +27 -0
- package/dist/core/causation/audit.js +103 -0
- package/dist/core/causation/aun-association.js +111 -0
- package/dist/core/causation/context.js +93 -0
- package/dist/core/causation/index.js +4 -0
- package/dist/core/causation/types.js +2 -0
- package/dist/core/channel-loader.js +277 -0
- package/dist/core/command/agent-control.js +616 -0
- package/dist/core/command/cli-intent-parser.js +225 -0
- package/dist/core/command/command-handler.js +1733 -0
- package/dist/core/command/connect-menu.js +374 -0
- package/dist/core/command/menu-handler.js +3452 -0
- package/dist/core/command/menu-protocol.js +247 -0
- package/dist/core/command/role-menu.js +1623 -0
- package/dist/core/command/slash-gate.js +148 -0
- package/dist/core/command/slash-handler.js +3066 -0
- package/dist/core/daemon-file-cache.js +222 -0
- package/dist/core/event-bus.js +32 -0
- package/dist/core/event-catalog.js +810 -0
- package/dist/core/evolagent-registry.js +545 -0
- package/dist/core/evolagent.js +342 -0
- package/dist/core/handoff/dispatcher.js +229 -0
- package/dist/core/handoff/mutex.js +46 -0
- package/dist/core/handoff/runtime.js +324 -0
- package/dist/core/handoff/store.js +537 -0
- package/dist/core/handoff/types.js +12 -0
- package/dist/core/inference/text-inference.js +173 -0
- package/dist/core/interaction-registration.js +10 -0
- package/dist/core/interaction-router.js +278 -0
- package/dist/core/message/create-status.js +67 -0
- package/dist/core/message/im-renderer.js +659 -0
- package/dist/core/message/items-formatter.js +76 -0
- package/dist/core/message/logical-queue-bridge.js +123 -0
- package/dist/core/message/message-bridge.js +874 -0
- package/dist/core/message/message-cache.js +56 -0
- package/dist/core/message/message-log.js +339 -0
- package/dist/core/message/message-processor.js +4 -0
- package/dist/core/message/message-queue.js +1446 -0
- package/dist/core/message/message-utils.js +76 -0
- package/dist/core/message/peer-mode.js +105 -0
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/message/response-depth.js +33 -0
- package/dist/core/message/response-engine.js +4020 -0
- package/dist/core/message/response-snapshot.js +83 -0
- package/dist/core/message/send-receipt.js +24 -0
- package/dist/core/message/stream-debouncer.js +139 -0
- package/dist/core/message/stream-idle-monitor.js +124 -0
- package/dist/core/model/config-scope.js +162 -0
- package/dist/core/model/field-scope.js +78 -0
- package/dist/core/model/model-catalog.js +227 -0
- package/dist/core/model/model-diagnostics.js +182 -0
- package/dist/core/model/model-permission.js +90 -0
- package/dist/core/permission/approval-gateway.js +1017 -0
- package/dist/core/permission/ec-command-parser.js +347 -0
- package/dist/core/permission/execution-sandbox.js +16 -0
- package/dist/core/permission/index.js +6 -0
- package/dist/core/permission/mode.js +24 -0
- package/dist/core/permission/sandbox-runtime.js +265 -0
- package/dist/core/permission/tool-policy.js +1019 -0
- package/dist/core/permission/unix-socket-policy.js +99 -0
- package/dist/core/protected-paths.js +332 -0
- package/dist/core/relation/peer-identity.js +222 -0
- package/dist/core/relation/peer-key.js +1 -0
- package/dist/core/role/runtime-policy.js +141 -0
- package/dist/core/session/adapters/claude-session-file-adapter.js +218 -0
- package/dist/core/session/adapters/codex-session-file-adapter.js +333 -0
- package/dist/core/session/adapters/gemini-session-file-adapter.js +181 -0
- package/dist/core/session/session-file-adapter.js +7 -0
- package/dist/core/session/session-file-health.js +45 -0
- package/dist/core/session/session-fs-store.js +273 -0
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +1643 -0
- package/dist/core/session/session-mapper.js +100 -0
- package/dist/core/session/session-renew.js +314 -0
- package/dist/core/session/session-title.js +128 -0
- package/dist/core/session/session-turn-coordinator.js +205 -0
- package/dist/core/session/session-turns.js +67 -0
- package/dist/core/system-channels.js +29 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/group-rules-sync.js +345 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/kit-renderer.js +359 -0
- package/dist/eck/manifest-engine.js +446 -0
- package/dist/eck/message-renderer.js +199 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +2926 -4
- package/dist/ipc.js +777 -0
- package/dist/paths.js +262 -0
- package/dist/product.js +18 -0
- package/dist/response-system/context-builder.js +71 -0
- package/dist/response-system/coordinator.js +117 -0
- package/dist/response-system/decision-executor.js +86 -0
- package/dist/response-system/engines/v1/index.js +21 -0
- package/dist/response-system/engines/v1/interactive-flow.js +27 -0
- package/dist/response-system/engines/v1/proactive-flow.js +137 -0
- package/dist/response-system/engines/v1/types.js +1 -0
- package/dist/response-system/extensions.js +41 -0
- package/dist/response-system/index.js +6 -0
- package/dist/response-system/modes/index.js +7 -0
- package/dist/response-system/modes/single-session/index.js +72 -0
- package/dist/response-system/queues/fifo-queue.js +44 -0
- package/dist/response-system/queues/index.js +6 -0
- package/dist/response-system/queues/lifo-queue.js +42 -0
- package/dist/response-system/queues/priority-queue.js +63 -0
- package/dist/response-system/registry.js +97 -0
- package/dist/response-system/resolver.js +37 -0
- package/dist/response-system/selector.js +23 -0
- package/dist/response-system/types.js +7 -0
- package/dist/stats/billing.js +163 -0
- package/dist/stats/budget.js +93 -0
- package/dist/stats/db.js +403 -0
- package/dist/stats/eck-vars.js +89 -0
- package/dist/stats/index.js +11 -0
- package/dist/stats/normalizer.js +80 -0
- package/dist/stats/price-resolver.js +138 -0
- package/dist/stats/query.js +763 -0
- package/dist/stats/role-budget.js +168 -0
- package/dist/stats/writer.js +151 -0
- package/dist/trigger/anomaly-store.js +258 -0
- package/dist/trigger/audit.js +152 -0
- package/dist/trigger/event-source.js +119 -0
- package/dist/trigger/feedback.js +685 -0
- package/dist/trigger/history.js +290 -0
- package/dist/trigger/manager.js +294 -0
- package/dist/trigger/parser.js +595 -0
- package/dist/trigger/patch.js +155 -0
- package/dist/trigger/scheduler.js +1602 -0
- package/dist/trigger/script-executor.js +155 -0
- package/dist/trigger/state.js +145 -0
- package/dist/trigger/types.js +1 -0
- package/dist/trigger/validation.js +634 -0
- package/dist/types.js +12 -0
- package/dist/utils/aid-bind.js +313 -0
- package/dist/utils/atomic-write.js +95 -0
- package/dist/utils/avatar-upload.js +123 -0
- package/dist/utils/cross-platform.js +297 -0
- package/dist/utils/ecweb-utils.js +73 -0
- package/dist/utils/error-dict.json +153 -0
- package/dist/utils/error-utils.js +349 -0
- package/dist/utils/instance-registry.js +444 -0
- package/dist/utils/locale.js +21 -0
- package/dist/utils/log-writer.js +270 -0
- package/dist/utils/logger.js +89 -0
- package/dist/utils/markdown-to-plain-text.js +20 -0
- package/dist/utils/media-cache.js +274 -0
- package/dist/utils/model-prices.jsonl +20 -0
- package/dist/utils/npm-ops.js +210 -0
- package/dist/utils/process-introspect.js +133 -0
- package/dist/utils/process-tree-stats.js +271 -0
- package/dist/utils/project-path.js +74 -0
- package/dist/utils/restart-safety.js +31 -0
- package/dist/utils/stats.js +410 -0
- package/dist/utils/system-memory.js +62 -0
- package/dist/utils/tool-summary.js +284 -0
- package/dist/utils/welcome.js +268 -0
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +66 -0
- package/kits/docs/aun/CHEATSHEET.md +19 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/aun.md +65 -0
- package/kits/docs/channels/feishu.md +56 -0
- package/kits/docs/context-assembly.md +366 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolcore/INDEX.md +68 -0
- package/kits/docs/evolcore/agent.md +77 -0
- package/kits/docs/evolcore/aid.md +52 -0
- package/kits/docs/evolcore/config.md +149 -0
- package/kits/docs/evolcore/contact.md +57 -0
- package/kits/docs/evolcore/ctl.md +46 -0
- package/kits/docs/evolcore/event.md +216 -0
- package/kits/docs/evolcore/fs-architecture.md +1215 -0
- package/kits/docs/evolcore/fs.md +110 -0
- package/kits/docs/evolcore/group-fs.md +17 -0
- package/kits/docs/evolcore/group-rules.md +226 -0
- package/kits/docs/evolcore/group.md +150 -0
- package/kits/docs/evolcore/model.md +50 -0
- package/kits/docs/evolcore/msg.md +136 -0
- package/kits/docs/evolcore/response.md +75 -0
- package/kits/docs/evolcore/rpc.md +37 -0
- package/kits/docs/evolcore/self-summary.md +29 -0
- package/kits/docs/evolcore/stats.md +83 -0
- package/kits/docs/evolcore/storage.md +50 -0
- package/kits/docs/evolcore/trigger.md +539 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +23 -0
- package/kits/docs/identity/identity-tools.md +26 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/docs/prompt-loading-architecture.md +266 -0
- package/kits/docs/venues/aun-group.md +45 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +25 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.auxiliary.json +43 -0
- package/kits/eck_manifest.json +203 -0
- package/kits/eck_message_manifest.json +63 -0
- package/kits/migrations/README-role-config-v4.md +32 -0
- package/kits/migrations/migrate-contact-book-v2.mjs +747 -0
- package/kits/migrations/migrate-role-config-v4.mjs +623 -0
- package/kits/migrations/migrate-role-config-v5.mjs +346 -0
- package/kits/migrations/rename-config-file.mjs +99 -0
- package/kits/rules/01-overview.md +142 -0
- package/kits/rules/02-navigation.md +76 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +59 -0
- package/kits/rules/05-venue.md +44 -0
- package/kits/rules/06-channel.md +59 -0
- package/kits/schemas/_meta.json +32 -0
- package/kits/schemas/agent-config.schema.1.json +177 -0
- package/kits/schemas/agent-config.schema.2.json +239 -0
- package/kits/schemas/agent-config.schema.3.json +119 -0
- package/kits/schemas/agent-config.schema.4.json +208 -0
- package/kits/schemas/agent-config.schema.5.json +326 -0
- package/kits/schemas/agent-config.schema.6.json +322 -0
- package/kits/schemas/contact-book.schema.1.json +36 -0
- package/kits/schemas/contact-book.schema.2.json +43 -0
- package/kits/schemas/daemon.schema.1.json +90 -0
- package/kits/schemas/defaults.schema.1.json +81 -0
- package/kits/schemas/menu-exec-schema-commands.md +208 -0
- package/kits/schemas/migrations/README.md +28 -0
- package/kits/schemas/relation-config.schema.1.json +158 -0
- package/kits/schemas/relation-config.schema.2.json +73 -0
- package/kits/schemas/relation-config.schema.3.json +50 -0
- package/kits/schemas/relation-config.schema.4.json +47 -0
- package/kits/schemas/relation-config.schema.5.json +47 -0
- package/kits/schemas/role-config.schema.1.json +201 -0
- package/kits/schemas/role-registry.schema.1.json +35 -0
- package/kits/schemas/single-session.schema.1.json +31 -0
- package/kits/templates/bootstrap-welcome.md +15 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +13 -0
- package/kits/templates/message-fragments/handoff-response-to-origin.md +10 -0
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/kits/templates/message-fragments/item.md +2 -0
- package/kits/templates/roles/admin.json +9 -0
- package/kits/templates/roles/member.json +41 -0
- package/kits/templates/roles/owner.json +9 -0
- package/kits/templates/roles/visitor.json +40 -0
- package/kits/templates/system-fragments/baseagent.md +14 -0
- package/kits/templates/system-fragments/bootstrap.md +16 -0
- package/kits/templates/system-fragments/channel.md +48 -0
- package/kits/templates/system-fragments/commands.md +28 -0
- package/kits/templates/system-fragments/identity.md +11 -0
- package/kits/templates/system-fragments/relation.md +19 -0
- package/kits/templates/system-fragments/session.md +53 -0
- package/kits/templates/system-fragments/venue.md +31 -0
- package/package.json +50 -15
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boot-log —— 启动日志与自检回落(design §九 + 双指针模型)。
|
|
3
|
+
*
|
|
4
|
+
* boot-log.jsonl:每次成功启动追加一行(最近 300 条,超限按月归档)。
|
|
5
|
+
* 自检模式:逐版本回落尝试,**展开版本到 W 并真实启动**(不是内存探测)。
|
|
6
|
+
* - P4:进入时若 W≠w-version,先把 W 存档(successCount=0)
|
|
7
|
+
* - 回落序列:从 current 往老遍历,跳过 successCount==0 的版本(例外:最新两个无条件参与)
|
|
8
|
+
* - 回落 = 展开版本到 W + writeWVersion,current 不动
|
|
9
|
+
* - Q-C:全失败 → 还原成最新版本
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { resolvePaths } from '../paths.js';
|
|
14
|
+
import { logger } from '../utils/logger.js';
|
|
15
|
+
import { readCurrent, readWVersion, writeWVersion, materializeVersion, listAllVersions, diffVersions, snapshot, isConfigSnapshotsEnabled, } from './snapshot.js';
|
|
16
|
+
const MAX_LINES = 300;
|
|
17
|
+
const KEEP_MIN = 150;
|
|
18
|
+
const FALLBACK_MAX_TRIES = 20;
|
|
19
|
+
const FALLBACK_MAX_MS = 120_000;
|
|
20
|
+
export function appendBootLog(entry) {
|
|
21
|
+
if (!isConfigSnapshotsEnabled())
|
|
22
|
+
return;
|
|
23
|
+
const p = resolvePaths().configBootLog;
|
|
24
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
25
|
+
fs.appendFileSync(p, JSON.stringify(entry) + '\n', 'utf-8');
|
|
26
|
+
archiveIfNeeded(p);
|
|
27
|
+
}
|
|
28
|
+
/** 读最近 n 条 boot-log(含归档回溯)。 */
|
|
29
|
+
export function readBootLog(n) {
|
|
30
|
+
const p = resolvePaths().configBootLog;
|
|
31
|
+
let lines = [];
|
|
32
|
+
try {
|
|
33
|
+
lines = fs.readFileSync(p, 'utf-8').split('\n').filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
catch { /* none */ }
|
|
36
|
+
if (lines.length < n) {
|
|
37
|
+
const archDir = resolvePaths().configBootLogArchiveDir;
|
|
38
|
+
if (fs.existsSync(archDir)) {
|
|
39
|
+
const archFiles = fs.readdirSync(archDir).filter(f => f.endsWith('.jsonl')).sort().reverse();
|
|
40
|
+
for (const f of archFiles) {
|
|
41
|
+
const more = fs.readFileSync(path.join(archDir, f), 'utf-8').split('\n').filter(Boolean);
|
|
42
|
+
lines = [...more, ...lines];
|
|
43
|
+
if (lines.length >= n)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return lines.slice(-n).map(l => { try {
|
|
49
|
+
return JSON.parse(l);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
} }).filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
function archiveIfNeeded(file) {
|
|
56
|
+
let lines;
|
|
57
|
+
try {
|
|
58
|
+
lines = fs.readFileSync(file, 'utf-8').split('\n').filter(Boolean);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (lines.length <= MAX_LINES)
|
|
64
|
+
return;
|
|
65
|
+
const archDir = resolvePaths().configBootLogArchiveDir;
|
|
66
|
+
fs.mkdirSync(archDir, { recursive: true });
|
|
67
|
+
const monthOf = (l) => {
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(l).bootedAt.slice(0, 7);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return 'unknown';
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const thisMonth = monthOf(lines[lines.length - 1]);
|
|
76
|
+
const keep = [];
|
|
77
|
+
const archived = {};
|
|
78
|
+
for (const l of lines) {
|
|
79
|
+
const m = monthOf(l);
|
|
80
|
+
if (m === thisMonth)
|
|
81
|
+
keep.push(l);
|
|
82
|
+
else
|
|
83
|
+
(archived[m] = archived[m] || []).push(l);
|
|
84
|
+
}
|
|
85
|
+
// 本月不足 KEEP_MIN,则从归档里补最新的回来
|
|
86
|
+
if (keep.length < KEEP_MIN) {
|
|
87
|
+
const flatArch = lines.filter(l => monthOf(l) !== thisMonth);
|
|
88
|
+
const need = KEEP_MIN - keep.length;
|
|
89
|
+
const backfill = flatArch.slice(-need);
|
|
90
|
+
keep.unshift(...backfill);
|
|
91
|
+
// 从 archived 中移除 backfill
|
|
92
|
+
const backfillSet = new Set(backfill);
|
|
93
|
+
for (const m of Object.keys(archived))
|
|
94
|
+
archived[m] = archived[m].filter(l => !backfillSet.has(l));
|
|
95
|
+
}
|
|
96
|
+
for (const [m, ls] of Object.entries(archived)) {
|
|
97
|
+
if (ls.length === 0)
|
|
98
|
+
continue;
|
|
99
|
+
fs.appendFileSync(path.join(archDir, `${m}.jsonl`), ls.join('\n') + '\n', 'utf-8');
|
|
100
|
+
}
|
|
101
|
+
fs.writeFileSync(file, keep.join('\n') + '\n', 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 自检模式:逐版本展开到 W,真实探测配置加载,找到第一个可用版本。
|
|
105
|
+
* - P4: W≠w-version → 先把 W 存档(保留你的改动,即使是坏的)
|
|
106
|
+
* - 回落序列: 从 current 往老,跳过 successCount==0(例外:序列中最新两个无条件参与)
|
|
107
|
+
* - 展开 = 覆盖 W + writeWVersion;current 始终不动
|
|
108
|
+
* - Q-C: 全失败 → 还原成最新版本(W 原样回来)
|
|
109
|
+
* probe: 注入点,默认用 loadAllAgents 检测配置层,也用于测试。
|
|
110
|
+
*/
|
|
111
|
+
export async function selfDiagnose(probe, now = () => Date.now()) {
|
|
112
|
+
if (!isConfigSnapshotsEnabled()) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
selectedVersion: readCurrent(),
|
|
116
|
+
actualVersion: readWVersion(),
|
|
117
|
+
fellBack: false,
|
|
118
|
+
triedVersions: [],
|
|
119
|
+
message: '配置快照功能已关闭;请先设置 configSnapshots=true',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const selected = readCurrent();
|
|
123
|
+
const wVer = readWVersion();
|
|
124
|
+
const root = resolvePaths().root;
|
|
125
|
+
// P4: W 有未存改动 → 先把 W 存档(坏的也存,保留改动)
|
|
126
|
+
// 判断:w-version 不存在,或 W 内容 ≠ w-version 指向的内容
|
|
127
|
+
const wHasChanges = !wVer || await workingDiffersFromVersion(wVer.delta);
|
|
128
|
+
if (wHasChanges) {
|
|
129
|
+
snapshot('manual', { description: 'diagnose: archive current W before fallback' });
|
|
130
|
+
// snapshot 已经更新了 current + w-version 到新版本
|
|
131
|
+
}
|
|
132
|
+
// 构建候选序列(从 current 往老遍历)
|
|
133
|
+
const allVersions = listAllVersions().sort((a, b) => cmpVer(b.version, a.version)); // 降序
|
|
134
|
+
const cur = readCurrent(); // P4 后重读(可能已更新)
|
|
135
|
+
const startIdx = cur ? allVersions.findIndex(m => m.version === cur.delta) : 0;
|
|
136
|
+
const sequence = startIdx >= 0 ? allVersions.slice(startIdx) : allVersions;
|
|
137
|
+
// 回落资格:successCount>0,或属序列中最新两个
|
|
138
|
+
const candidates = sequence.filter((m, i) => i < 2 || m.successCount > 0);
|
|
139
|
+
if (candidates.length === 0) {
|
|
140
|
+
const latest = allVersions[0]?.version;
|
|
141
|
+
if (latest)
|
|
142
|
+
await expandToW(latest, root);
|
|
143
|
+
return {
|
|
144
|
+
ok: false, selectedVersion: selected, actualVersion: null, fellBack: false, triedVersions: [],
|
|
145
|
+
message: '✗ 自检失败:无可用候选版本(所有版本 successCount=0)。已还原到最新版本。',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const probeFn = probe ?? await makeDefaultProbe();
|
|
149
|
+
const tried = [];
|
|
150
|
+
const t0 = now();
|
|
151
|
+
let tryCount = 0;
|
|
152
|
+
for (const candidate of candidates) {
|
|
153
|
+
if (tryCount > 0) {
|
|
154
|
+
if (tryCount > FALLBACK_MAX_TRIES) {
|
|
155
|
+
await expandToW(allVersions[0].version, root); // Q-C
|
|
156
|
+
return breakerResult('tries', selected, tried);
|
|
157
|
+
}
|
|
158
|
+
if (now() - t0 > FALLBACK_MAX_MS) {
|
|
159
|
+
await expandToW(allVersions[0].version, root); // Q-C
|
|
160
|
+
return breakerResult('time', selected, tried);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
tryCount++;
|
|
164
|
+
tried.push(candidate.version);
|
|
165
|
+
// 展开到 W
|
|
166
|
+
await expandToW(candidate.version, root);
|
|
167
|
+
let ok = false;
|
|
168
|
+
try {
|
|
169
|
+
ok = probeFn();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
ok = false;
|
|
173
|
+
}
|
|
174
|
+
if (ok) {
|
|
175
|
+
const actual = findVersionPtr(candidate.version, allVersions);
|
|
176
|
+
const fellBack = !selected || selected.delta !== candidate.version;
|
|
177
|
+
if (fellBack)
|
|
178
|
+
printFallbackInfo(selected, actual);
|
|
179
|
+
return { ok: true, selectedVersion: selected, actualVersion: actual, fellBack, triedVersions: tried };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Q-C: 全失败 → 还原最新版本
|
|
183
|
+
if (allVersions.length > 0)
|
|
184
|
+
await expandToW(allVersions[0].version, root);
|
|
185
|
+
return {
|
|
186
|
+
ok: false, selectedVersion: selected, actualVersion: null, fellBack: false, triedVersions: tried,
|
|
187
|
+
message: `✗ 自检失败:已尝试 ${tried.length} 个候选版本(${tried.join('→')})仍无可用。已还原最新版本。`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async function expandToW(version, root) {
|
|
191
|
+
const all = listAllVersions();
|
|
192
|
+
const m = all.find(x => x.version === version);
|
|
193
|
+
if (!m)
|
|
194
|
+
return;
|
|
195
|
+
const full = m.baseVersion || version;
|
|
196
|
+
const files = materializeVersion(full, version);
|
|
197
|
+
// 删除 W 中不在目标版本的配置文件
|
|
198
|
+
const { collectConfigFiles } = await import('./snapshot.js');
|
|
199
|
+
const existing = collectConfigFiles(root);
|
|
200
|
+
for (const rel of existing) {
|
|
201
|
+
if (!files.has(rel)) {
|
|
202
|
+
try {
|
|
203
|
+
fs.unlinkSync(path.join(root, rel));
|
|
204
|
+
}
|
|
205
|
+
catch { /* */ }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
for (const [rel, content] of files) {
|
|
209
|
+
const dest = path.join(root, rel);
|
|
210
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
211
|
+
fs.writeFileSync(dest, content, 'utf-8');
|
|
212
|
+
}
|
|
213
|
+
writeWVersion({ full, delta: version });
|
|
214
|
+
}
|
|
215
|
+
async function workingDiffersFromVersion(version) {
|
|
216
|
+
const { diffWorkingVsVersion } = await import('./snapshot.js');
|
|
217
|
+
const d = diffWorkingVsVersion(version);
|
|
218
|
+
if ('error' in d)
|
|
219
|
+
return true;
|
|
220
|
+
return d.added.length + d.modified.length + d.deleted.length > 0;
|
|
221
|
+
}
|
|
222
|
+
async function makeDefaultProbe() {
|
|
223
|
+
// 延迟 import 避免循环依赖:config-store → config,boot-log 在 config
|
|
224
|
+
const { loadAllAgents } = await import('../config-store.js');
|
|
225
|
+
return () => {
|
|
226
|
+
try {
|
|
227
|
+
const { agents } = loadAllAgents();
|
|
228
|
+
return agents.length > 0;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function cmpVer(a, b) {
|
|
236
|
+
return parseInt(a.slice(1)) - parseInt(b.slice(1));
|
|
237
|
+
}
|
|
238
|
+
function findVersionPtr(version, all) {
|
|
239
|
+
const m = all.find(x => x.version === version);
|
|
240
|
+
return { full: m?.baseVersion || version, delta: version };
|
|
241
|
+
}
|
|
242
|
+
function breakerResult(kind, selected, tried) {
|
|
243
|
+
const msg = kind === 'tries'
|
|
244
|
+
? `✗ 自检失败:回落尝试达到 ${FALLBACK_MAX_TRIES} 次上限(${tried.join('→')})。已还原最新版本。`
|
|
245
|
+
: `✗ 自检失败:回落耗时超过 2 分钟(已尝试 ${tried.join('→')})。已还原最新版本。`;
|
|
246
|
+
return { ok: false, selectedVersion: selected, actualVersion: null, fellBack: false, triedVersions: tried, breaker: kind, message: msg };
|
|
247
|
+
}
|
|
248
|
+
function printFallbackInfo(selected, actual) {
|
|
249
|
+
const lines = [`⚠ 当前版本 ${selected?.delta ?? '?'} 启动失败,已回落到 ${actual.delta}。`];
|
|
250
|
+
// 参数级差异:actual(W现状) vs selected(失败版本)
|
|
251
|
+
if (selected) {
|
|
252
|
+
const d = diffVersions(actual.delta, selected.delta);
|
|
253
|
+
if (!('error' in d)) {
|
|
254
|
+
const changed = [...d.modified, ...d.added, ...d.deleted];
|
|
255
|
+
if (changed.length)
|
|
256
|
+
lines.push(` 变化文件:${changed.join(', ')}`);
|
|
257
|
+
}
|
|
258
|
+
lines.push(` 失败版本 vs 回落版本的参数差异:ec config diff ${actual.delta} ${selected.delta}`);
|
|
259
|
+
}
|
|
260
|
+
lines.push(` current.json 仍指向 ${selected?.delta ?? '?'}(未改写)。`);
|
|
261
|
+
lines.push(` 采用回落版本:ec config restore ${actual.delta}`);
|
|
262
|
+
logger.warn(lines.join('\n'));
|
|
263
|
+
console.error(lines.join('\n'));
|
|
264
|
+
}
|
|
265
|
+
/** restore 回落版本到工作目录(采用某可用版本时调用)。委托 snapshot.restore。 */
|
|
266
|
+
export { readCurrent };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { kitsRoleTemplatesDir } from '../paths.js';
|
|
4
|
+
import { validateRoleConfig } from './role-schema.js';
|
|
5
|
+
import { expectedRoleRank } from './role-ranks.js';
|
|
6
|
+
export const BUILTIN_ROLE_IDS = ['owner', 'admin', 'member', 'visitor'];
|
|
7
|
+
export function validateBuiltinRoleTemplateDefinition(roleId, raw) {
|
|
8
|
+
const issues = validateRoleConfig(raw);
|
|
9
|
+
if (issues.length) {
|
|
10
|
+
return {
|
|
11
|
+
definition: null,
|
|
12
|
+
error: `built-in role template failed validation: ${issues.map(issue => `${issue.field} ${issue.message}`).join('; ')}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const { $schema_version: _schemaVersion, ...definition } = raw;
|
|
16
|
+
if (BUILTIN_ROLE_IDS.includes(roleId)) {
|
|
17
|
+
const expectedRank = expectedRoleRank(roleId);
|
|
18
|
+
if (definition.rank !== expectedRank) {
|
|
19
|
+
return {
|
|
20
|
+
definition: null,
|
|
21
|
+
error: `built-in role template rank must be ${expectedRank}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { definition: definition };
|
|
26
|
+
}
|
|
27
|
+
/** Load the versioned package template that defines a built-in role policy. */
|
|
28
|
+
export function readBuiltinRoleTemplate(roleId) {
|
|
29
|
+
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 };
|
|
42
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readBuiltinRoleTemplate } from './builtin-role-templates.js';
|
|
2
|
+
import { expectedRoleRank } from './role-ranks.js';
|
|
3
|
+
export { expectedRoleRank, ROLE_RANKS } from './role-ranks.js';
|
|
4
|
+
export const BUILTIN_USER_ROLES = ['member', 'visitor'];
|
|
5
|
+
export const MANAGEMENT_ROLES = ['owner', 'admin'];
|
|
6
|
+
export function isManagementRole(role) {
|
|
7
|
+
return role === 'owner' || role === 'admin';
|
|
8
|
+
}
|
|
9
|
+
export function isReservedRoleName(role) {
|
|
10
|
+
return isManagementRole(role);
|
|
11
|
+
}
|
|
12
|
+
/** Built-in user policy data comes exclusively from kits/templates/roles. */
|
|
13
|
+
export function getBuiltinRolesConfig() {
|
|
14
|
+
return {
|
|
15
|
+
$schema_version: 1,
|
|
16
|
+
defaultRoles: { private: null, group: null },
|
|
17
|
+
roles: Object.fromEntries(BUILTIN_USER_ROLES.flatMap(roleId => {
|
|
18
|
+
const definition = readBuiltinRoleTemplate(roleId).definition;
|
|
19
|
+
return definition ? [[roleId, definition]] : [];
|
|
20
|
+
})),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
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.
|
|
26
|
+
*/
|
|
27
|
+
export function getManagementCommandPermissions(role) {
|
|
28
|
+
return {
|
|
29
|
+
'contact.read': { allow: true, scopes: ['agent'] },
|
|
30
|
+
'contact.block': { allow: true, scopes: ['agent'] },
|
|
31
|
+
'role.assign': { allow: true, scopes: ['agent'] },
|
|
32
|
+
'role.revoke': { allow: true, scopes: ['agent'] },
|
|
33
|
+
'role.policy.read': { allow: true, scopes: ['agent'] },
|
|
34
|
+
'role.relation.read': { allow: true, scopes: ['agent'] },
|
|
35
|
+
'role.policy.write': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
|
|
36
|
+
'*': { allow: true },
|
|
37
|
+
'agent.reload': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
|
|
38
|
+
'dangerous:*': { allow: true, dangerous: true, constraints: { requireDaemonOwner: true } },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function applyManagementRoleCeiling(role, configured) {
|
|
42
|
+
const fallback = {
|
|
43
|
+
description: role === 'owner' ? 'Agent owner' : 'Agent administrator',
|
|
44
|
+
rank: expectedRoleRank(role),
|
|
45
|
+
allowAccess: true,
|
|
46
|
+
permissions: {
|
|
47
|
+
permissionMode: { default: 'request', allowOverride: false },
|
|
48
|
+
},
|
|
49
|
+
commandPermissions: getManagementCommandPermissions(role),
|
|
50
|
+
};
|
|
51
|
+
const policy = configured ?? readBuiltinRoleTemplate(role).definition ?? fallback;
|
|
52
|
+
return {
|
|
53
|
+
...policy,
|
|
54
|
+
rank: expectedRoleRank(role),
|
|
55
|
+
allowAccess: true,
|
|
56
|
+
commandPermissions: narrowManagementCommandPermissions(getManagementCommandPermissions(role), policy.commandPermissions || {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function getManagementRoleDefinition(role) {
|
|
60
|
+
return applyManagementRoleCeiling(role);
|
|
61
|
+
}
|
|
62
|
+
function narrowManagementCommandPermissions(ceiling, configured) {
|
|
63
|
+
const merged = {};
|
|
64
|
+
for (const [operation, permission] of Object.entries(ceiling)) {
|
|
65
|
+
const override = configured[operation];
|
|
66
|
+
if (!override || typeof override !== 'object') {
|
|
67
|
+
merged[operation] = { ...permission };
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const next = { ...permission };
|
|
71
|
+
if (override.allow === false)
|
|
72
|
+
next.allow = false;
|
|
73
|
+
if (override.dangerous === false)
|
|
74
|
+
next.dangerous = false;
|
|
75
|
+
if (Array.isArray(permission.scopes) && Array.isArray(override.scopes)) {
|
|
76
|
+
next.scopes = permission.scopes.filter(scope => override.scopes.includes(scope));
|
|
77
|
+
}
|
|
78
|
+
else if (!Array.isArray(permission.scopes) && Array.isArray(override.scopes)) {
|
|
79
|
+
next.scopes = [...override.scopes];
|
|
80
|
+
}
|
|
81
|
+
merged[operation] = next;
|
|
82
|
+
}
|
|
83
|
+
// Exact or namespace denies may narrow grants inherited through the ceiling's
|
|
84
|
+
// wildcard. New allow rules are ignored because they could bypass ceiling constraints.
|
|
85
|
+
for (const [operation, permission] of Object.entries(configured)) {
|
|
86
|
+
if (!Object.hasOwn(merged, operation) && permission?.allow === false) {
|
|
87
|
+
merged[operation] = { ...permission, allow: false };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return merged;
|
|
91
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { normalizeCliArgv } from '../cli/cli-argv.js';
|
|
2
|
+
export function splitConfigBatchGetArgv(inputArgv) {
|
|
3
|
+
const argv = normalizeCliArgv(inputArgv);
|
|
4
|
+
if (argv[0] !== 'config' || argv[1] !== 'get')
|
|
5
|
+
return null;
|
|
6
|
+
const fields = [];
|
|
7
|
+
let i = 2;
|
|
8
|
+
for (; i < argv.length && !argv[i].startsWith('-'); i++)
|
|
9
|
+
fields.push(argv[i]);
|
|
10
|
+
return { fields, rest: argv.slice(i) };
|
|
11
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
const BEHAVIOR_TOP_FIELDS = new Set([
|
|
2
|
+
'active_baseagent',
|
|
3
|
+
'chatmode',
|
|
4
|
+
'mentionMode',
|
|
5
|
+
'responseMode',
|
|
6
|
+
'responseModeParams',
|
|
7
|
+
'flush_delay',
|
|
8
|
+
'debounce',
|
|
9
|
+
'show_activities',
|
|
10
|
+
'session_renew',
|
|
11
|
+
'group_venue_sync',
|
|
12
|
+
'render',
|
|
13
|
+
'sessionManifests',
|
|
14
|
+
'enable_rich_content',
|
|
15
|
+
]);
|
|
16
|
+
const BASEAGENT_BEHAVIOR_FIELDS = new Set([
|
|
17
|
+
'model',
|
|
18
|
+
'effort',
|
|
19
|
+
'reasoning',
|
|
20
|
+
'agentProgressSummaries',
|
|
21
|
+
'excludeDynamicSections',
|
|
22
|
+
'enableRequestUserInput',
|
|
23
|
+
'approvalsReviewer',
|
|
24
|
+
'mode',
|
|
25
|
+
'useVertex',
|
|
26
|
+
]);
|
|
27
|
+
const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'hermes']);
|
|
28
|
+
const SENSITIVE_TOP_FIELDS = new Set([
|
|
29
|
+
'$schema_version',
|
|
30
|
+
'aid',
|
|
31
|
+
'enabled',
|
|
32
|
+
'lifecycle',
|
|
33
|
+
'initialized',
|
|
34
|
+
'owners',
|
|
35
|
+
'admins',
|
|
36
|
+
'channels',
|
|
37
|
+
'aun',
|
|
38
|
+
'models',
|
|
39
|
+
'projects',
|
|
40
|
+
'capabilities',
|
|
41
|
+
'debug',
|
|
42
|
+
'observable',
|
|
43
|
+
'roles',
|
|
44
|
+
'role',
|
|
45
|
+
'extra_backup',
|
|
46
|
+
]);
|
|
47
|
+
const CRITICAL_AGENT_FIELDS = new Set(['owners', 'admins', 'roles']);
|
|
48
|
+
const EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
49
|
+
function scalar(rolePermissionKey, valueKind) {
|
|
50
|
+
return { class: 'safe-scalar', rolePermissionKey, valueKind };
|
|
51
|
+
}
|
|
52
|
+
function readonlyObject(rolePermissionKey) {
|
|
53
|
+
return { class: 'safe-readonly-object', rolePermissionKey };
|
|
54
|
+
}
|
|
55
|
+
export function isBehaviorConfigFieldPath(fieldPath) {
|
|
56
|
+
const parts = fieldPath.split('.');
|
|
57
|
+
if (parts[0] === 'baseagents') {
|
|
58
|
+
return parts.length >= 3 && BASEAGENT_BEHAVIOR_FIELDS.has(parts[2]);
|
|
59
|
+
}
|
|
60
|
+
return BEHAVIOR_TOP_FIELDS.has(parts[0]);
|
|
61
|
+
}
|
|
62
|
+
export function resolveConfigFieldRule(fieldPath) {
|
|
63
|
+
const field = fieldPath.trim();
|
|
64
|
+
if (!field)
|
|
65
|
+
return { class: 'unknown' };
|
|
66
|
+
const parts = field.split('.');
|
|
67
|
+
const top = parts[0];
|
|
68
|
+
if (SENSITIVE_TOP_FIELDS.has(top))
|
|
69
|
+
return { class: 'sensitive' };
|
|
70
|
+
if (top === 'baseagents') {
|
|
71
|
+
if (parts.length < 3)
|
|
72
|
+
return { class: 'sensitive' };
|
|
73
|
+
const [, baseagent, leaf] = parts;
|
|
74
|
+
if (!SUPPORTED_BASEAGENTS.has(baseagent))
|
|
75
|
+
return { class: 'unknown' };
|
|
76
|
+
const permissionKey = `baseagents.${baseagent}.${leaf}`;
|
|
77
|
+
if (leaf === 'model' && parts.length === 3)
|
|
78
|
+
return scalar(permissionKey, 'string');
|
|
79
|
+
if ((leaf === 'effort' || leaf === 'reasoning') && parts.length === 3)
|
|
80
|
+
return scalar(permissionKey, 'effort');
|
|
81
|
+
if (baseagent === 'claude' && (leaf === 'agentProgressSummaries' || leaf === 'excludeDynamicSections') && parts.length === 3) {
|
|
82
|
+
return scalar(permissionKey, 'boolean');
|
|
83
|
+
}
|
|
84
|
+
if (baseagent === 'codex' && leaf === 'enableRequestUserInput' && parts.length === 3) {
|
|
85
|
+
return scalar(permissionKey, 'boolean');
|
|
86
|
+
}
|
|
87
|
+
if (baseagent === 'codex' && leaf === 'approvalsReviewer' && parts.length === 3) {
|
|
88
|
+
return scalar(permissionKey, 'approvals-reviewer');
|
|
89
|
+
}
|
|
90
|
+
if (baseagent === 'gemini' && leaf === 'mode' && parts.length === 3)
|
|
91
|
+
return scalar(permissionKey, 'gemini-mode');
|
|
92
|
+
if (baseagent === 'gemini' && leaf === 'useVertex' && parts.length === 3)
|
|
93
|
+
return scalar(permissionKey, 'boolean');
|
|
94
|
+
return { class: 'sensitive' };
|
|
95
|
+
}
|
|
96
|
+
if (field === 'active_baseagent')
|
|
97
|
+
return scalar(field, 'baseagent');
|
|
98
|
+
if (field === 'flush_delay' || field === 'debounce')
|
|
99
|
+
return scalar(field, 'nonnegative-number');
|
|
100
|
+
if (field === 'mentionMode')
|
|
101
|
+
return scalar(field, 'mention-mode');
|
|
102
|
+
if (field === 'show_activities')
|
|
103
|
+
return scalar(field, 'show-activities');
|
|
104
|
+
if (field === 'enable_rich_content')
|
|
105
|
+
return scalar(field, 'boolean');
|
|
106
|
+
if (field === 'responseMode')
|
|
107
|
+
return scalar(field, 'string');
|
|
108
|
+
if (field === 'configSnapshots')
|
|
109
|
+
return scalar('configSnapshots', 'boolean');
|
|
110
|
+
if (top === 'chatmode') {
|
|
111
|
+
if (parts.length === 1)
|
|
112
|
+
return readonlyObject('chatmode');
|
|
113
|
+
if (parts.length === 2 && ['private', 'group', 'nothuman'].includes(parts[1])) {
|
|
114
|
+
return scalar('chatmode', 'chatmode');
|
|
115
|
+
}
|
|
116
|
+
return { class: 'unknown' };
|
|
117
|
+
}
|
|
118
|
+
if (top === 'responseMode') {
|
|
119
|
+
if (parts.length === 1)
|
|
120
|
+
return scalar('responseMode', 'string');
|
|
121
|
+
return { class: 'unknown' };
|
|
122
|
+
}
|
|
123
|
+
if (top === 'responseModeParams') {
|
|
124
|
+
// responseModeParams 是按模式分桶的字典,整体只读,子路径黑箱
|
|
125
|
+
return readonlyObject('responseModeParams');
|
|
126
|
+
}
|
|
127
|
+
if (top === 'session_renew') {
|
|
128
|
+
if (parts.length === 1)
|
|
129
|
+
return readonlyObject('session_renew');
|
|
130
|
+
if (parts.length !== 2)
|
|
131
|
+
return { class: 'unknown' };
|
|
132
|
+
if (parts[1] === 'enabled')
|
|
133
|
+
return scalar('session_renew', 'boolean');
|
|
134
|
+
if (parts[1] === 'after_hours')
|
|
135
|
+
return scalar('session_renew', 'positive-number');
|
|
136
|
+
if (parts[1] === 'effort')
|
|
137
|
+
return scalar('session_renew', 'effort');
|
|
138
|
+
if (parts[1] === 'fallback_action')
|
|
139
|
+
return scalar('session_renew', 'session-renew-action');
|
|
140
|
+
return { class: 'unknown' };
|
|
141
|
+
}
|
|
142
|
+
if (top === 'render') {
|
|
143
|
+
if (parts.length === 1)
|
|
144
|
+
return readonlyObject('render');
|
|
145
|
+
if (parts.length === 2 && ['private', 'group', 'inject'].includes(parts[1]))
|
|
146
|
+
return scalar('render', 'string');
|
|
147
|
+
return { class: 'unknown' };
|
|
148
|
+
}
|
|
149
|
+
if (top === 'sessionManifests') {
|
|
150
|
+
if (parts.length === 1)
|
|
151
|
+
return readonlyObject('sessionManifests');
|
|
152
|
+
if (parts.length === 2 && /^[A-Za-z0-9_-]+$/.test(parts[1]))
|
|
153
|
+
return scalar('sessionManifests', 'manifest-file');
|
|
154
|
+
return { class: 'unknown' };
|
|
155
|
+
}
|
|
156
|
+
if (top === 'group_venue_sync')
|
|
157
|
+
return parts.length === 1
|
|
158
|
+
? readonlyObject('group_venue_sync')
|
|
159
|
+
: { class: 'unknown' };
|
|
160
|
+
return { class: 'unknown' };
|
|
161
|
+
}
|
|
162
|
+
export function isSafeBehaviorConfigField(fieldPath) {
|
|
163
|
+
const rule = resolveConfigFieldRule(fieldPath);
|
|
164
|
+
return rule.class === 'safe-scalar' || rule.class === 'safe-readonly-object';
|
|
165
|
+
}
|
|
166
|
+
export function isCriticalAgentControlField(fieldPath) {
|
|
167
|
+
return CRITICAL_AGENT_FIELDS.has(fieldPath.split('.')[0]);
|
|
168
|
+
}
|
|
169
|
+
export function resolveRoleFieldPermission(permissions, fieldPath) {
|
|
170
|
+
const rule = resolveConfigFieldRule(fieldPath);
|
|
171
|
+
if (!rule.rolePermissionKey)
|
|
172
|
+
return undefined;
|
|
173
|
+
return permissions[fieldPath] ?? permissions[rule.rolePermissionKey];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Map relation-scoped config fields back to their public command capability.
|
|
177
|
+
*
|
|
178
|
+
* The generic `config set/unset` transport must not become a permission bypass
|
|
179
|
+
* for the structured Menu operations that own these fields.
|
|
180
|
+
*/
|
|
181
|
+
export function relationFieldWriteOperation(fieldPath) {
|
|
182
|
+
const field = fieldPath.trim();
|
|
183
|
+
if (field === 'chatmode.private' || field === 'chatmode.group') {
|
|
184
|
+
return 'chatmode.update';
|
|
185
|
+
}
|
|
186
|
+
if (field === 'mentionMode')
|
|
187
|
+
return 'mentionmode.update';
|
|
188
|
+
if (/^baseagents\.[^.]+\.model$/.test(field))
|
|
189
|
+
return 'model.use';
|
|
190
|
+
if (/^baseagents\.[^.]+\.(?:effort|reasoning)$/.test(field)) {
|
|
191
|
+
return 'model.effort';
|
|
192
|
+
}
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
export function parseConfigFieldValue(fieldPath, rawValue) {
|
|
196
|
+
const rule = resolveConfigFieldRule(fieldPath);
|
|
197
|
+
if (rule.class !== 'safe-scalar' || !rule.valueKind) {
|
|
198
|
+
return { ok: false, reason: `Config field ${fieldPath} does not support scalar writes` };
|
|
199
|
+
}
|
|
200
|
+
if (typeof rawValue !== 'string')
|
|
201
|
+
return { ok: false, reason: `Config field ${fieldPath} requires a string CLI value` };
|
|
202
|
+
const raw = rawValue.trim();
|
|
203
|
+
switch (rule.valueKind) {
|
|
204
|
+
case 'string':
|
|
205
|
+
return raw ? { ok: true, value: raw } : { ok: false, reason: `Config field ${fieldPath} cannot be empty` };
|
|
206
|
+
case 'baseagent':
|
|
207
|
+
return SUPPORTED_BASEAGENTS.has(raw)
|
|
208
|
+
? { ok: true, value: raw }
|
|
209
|
+
: { ok: false, reason: `Config field ${fieldPath} must name a supported baseagent` };
|
|
210
|
+
case 'boolean':
|
|
211
|
+
if (raw === 'true')
|
|
212
|
+
return { ok: true, value: true };
|
|
213
|
+
if (raw === 'false')
|
|
214
|
+
return { ok: true, value: false };
|
|
215
|
+
return { ok: false, reason: `Config field ${fieldPath} must be true or false` };
|
|
216
|
+
case 'nonnegative-number': {
|
|
217
|
+
const value = Number(raw);
|
|
218
|
+
return Number.isFinite(value) && value >= 0
|
|
219
|
+
? { ok: true, value }
|
|
220
|
+
: { ok: false, reason: `Config field ${fieldPath} must be a non-negative number` };
|
|
221
|
+
}
|
|
222
|
+
case 'positive-number': {
|
|
223
|
+
const value = Number(raw);
|
|
224
|
+
return Number.isFinite(value) && value > 0
|
|
225
|
+
? { ok: true, value }
|
|
226
|
+
: { ok: false, reason: `Config field ${fieldPath} must be a positive number` };
|
|
227
|
+
}
|
|
228
|
+
case 'effort':
|
|
229
|
+
return EFFORTS.has(raw)
|
|
230
|
+
? { ok: true, value: raw }
|
|
231
|
+
: { ok: false, reason: `Config field ${fieldPath} must be one of ${[...EFFORTS].join(', ')}; use unset for auto` };
|
|
232
|
+
case 'chatmode':
|
|
233
|
+
return raw === 'interactive' || raw === 'proactive'
|
|
234
|
+
? { ok: true, value: raw }
|
|
235
|
+
: { ok: false, reason: `Config field ${fieldPath} must be interactive or proactive` };
|
|
236
|
+
case 'mention-mode':
|
|
237
|
+
return raw === 'disabled' || raw === 'mention-only'
|
|
238
|
+
? { ok: true, value: raw }
|
|
239
|
+
: { ok: false, reason: `Config field ${fieldPath} must be disabled or mention-only` };
|
|
240
|
+
case 'show-activities':
|
|
241
|
+
return raw === 'all' || raw === 'text' || raw === 'none'
|
|
242
|
+
? { ok: true, value: raw }
|
|
243
|
+
: { ok: false, reason: `Config field ${fieldPath} must be all, text, or none` };
|
|
244
|
+
case 'approvals-reviewer':
|
|
245
|
+
return ['user', 'auto_review', 'guardian_subagent'].includes(raw)
|
|
246
|
+
? { ok: true, value: raw }
|
|
247
|
+
: { ok: false, reason: `Config field ${fieldPath} has an invalid approvals reviewer` };
|
|
248
|
+
case 'gemini-mode':
|
|
249
|
+
return raw === 'cli' || raw === 'sdk'
|
|
250
|
+
? { ok: true, value: raw }
|
|
251
|
+
: { ok: false, reason: `Config field ${fieldPath} must be cli or sdk` };
|
|
252
|
+
case 'manifest-file':
|
|
253
|
+
return /^[A-Za-z0-9_-]+\.json$/.test(raw)
|
|
254
|
+
? { ok: true, value: raw }
|
|
255
|
+
: { ok: false, reason: `Config field ${fieldPath} must be a JSON filename without path separators` };
|
|
256
|
+
case 'session-renew-action':
|
|
257
|
+
return raw === 'continue' || raw === 'new'
|
|
258
|
+
? { ok: true, value: raw }
|
|
259
|
+
: { ok: false, reason: `Config field ${fieldPath} must be continue or new` };
|
|
260
|
+
}
|
|
261
|
+
}
|