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,634 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import { normalizePermissionMode as normalizePermissionModeContract } from '../core/permission/mode.js';
|
|
4
|
+
import { formatChannelKey } from '../core/channel-loader.js';
|
|
5
|
+
const MAX_SCRIPT_TIMEOUT_MS = 900_000;
|
|
6
|
+
export const TRIGGER_SCHEMA_VERSION = 3.1;
|
|
7
|
+
const LEGACY_TRIGGER_SCHEMA_VERSION = 4;
|
|
8
|
+
const DEFAULT_NOOP_SENTINEL = '[[NOOP]]';
|
|
9
|
+
const LIMIT_DURATION_RE = /^[1-9]\d*(s|m|h|d)$/;
|
|
10
|
+
const TRIGGER_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
11
|
+
const PERMISSION_MODES = new Set([
|
|
12
|
+
'readonly',
|
|
13
|
+
'auto',
|
|
14
|
+
'request',
|
|
15
|
+
'bypass',
|
|
16
|
+
]);
|
|
17
|
+
export function normalizeTriggerDefinition(input, opts = {}) {
|
|
18
|
+
if (!isObject(input))
|
|
19
|
+
throw new Error('trigger definition must be an object');
|
|
20
|
+
const raw = input;
|
|
21
|
+
const version = raw.$schema_version;
|
|
22
|
+
if (version === undefined)
|
|
23
|
+
throw new Error('trigger definition missing required field: $schema_version');
|
|
24
|
+
if (version !== TRIGGER_SCHEMA_VERSION && version !== LEGACY_TRIGGER_SCHEMA_VERSION) {
|
|
25
|
+
throw new Error(`trigger schema version ${TRIGGER_SCHEMA_VERSION} is required; got ${version}`);
|
|
26
|
+
}
|
|
27
|
+
return normalizeV3Phase2(raw, opts);
|
|
28
|
+
}
|
|
29
|
+
export function validateTriggerDefinition(definition) {
|
|
30
|
+
normalizeTriggerDefinition(definition);
|
|
31
|
+
}
|
|
32
|
+
export function definitionRevision(definition) {
|
|
33
|
+
const stable = stableStringify(definition);
|
|
34
|
+
return `sha256:${sha256(stable)}`;
|
|
35
|
+
}
|
|
36
|
+
export function sha256(value) {
|
|
37
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
38
|
+
}
|
|
39
|
+
export function previewText(value, max = 240) {
|
|
40
|
+
const cleaned = value.replace(/\s+/g, ' ').trim();
|
|
41
|
+
return cleaned.length > max ? `${cleaned.slice(0, max)}...` : cleaned;
|
|
42
|
+
}
|
|
43
|
+
export function splitScriptCommand(input) {
|
|
44
|
+
const text = String(input ?? '').trim();
|
|
45
|
+
if (!text)
|
|
46
|
+
return { path: '', args: [] };
|
|
47
|
+
const parts = [];
|
|
48
|
+
let current = '';
|
|
49
|
+
let quote;
|
|
50
|
+
let escaping = false;
|
|
51
|
+
for (const ch of text) {
|
|
52
|
+
if (escaping) {
|
|
53
|
+
current += ch;
|
|
54
|
+
escaping = false;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (ch === '\\') {
|
|
58
|
+
escaping = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (quote) {
|
|
62
|
+
if (ch === quote)
|
|
63
|
+
quote = undefined;
|
|
64
|
+
else
|
|
65
|
+
current += ch;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (ch === '"' || ch === "'") {
|
|
69
|
+
quote = ch;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (/\s/.test(ch)) {
|
|
73
|
+
if (current) {
|
|
74
|
+
parts.push(current);
|
|
75
|
+
current = '';
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
current += ch;
|
|
80
|
+
}
|
|
81
|
+
if (escaping)
|
|
82
|
+
current += '\\';
|
|
83
|
+
if (quote)
|
|
84
|
+
throw new Error('script.path has an unterminated quoted argument');
|
|
85
|
+
if (current)
|
|
86
|
+
parts.push(current);
|
|
87
|
+
return { path: parts[0] ?? '', args: parts.slice(1) };
|
|
88
|
+
}
|
|
89
|
+
export function resolveScriptPath(triggerDir, scriptPath) {
|
|
90
|
+
const command = splitScriptCommand(scriptPath);
|
|
91
|
+
scriptPath = command.path;
|
|
92
|
+
if (path.isAbsolute(scriptPath)) {
|
|
93
|
+
throw new Error('script.path must be relative to trigger directory');
|
|
94
|
+
}
|
|
95
|
+
const normalized = scriptPath.replace(/\\/g, '/');
|
|
96
|
+
if (!normalized || normalized.includes('\0')) {
|
|
97
|
+
throw new Error('script.path is invalid');
|
|
98
|
+
}
|
|
99
|
+
const absolute = path.resolve(triggerDir, normalized);
|
|
100
|
+
const root = path.resolve(triggerDir);
|
|
101
|
+
const rel = path.relative(root, absolute);
|
|
102
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
103
|
+
throw new Error('script.path must stay inside trigger directory');
|
|
104
|
+
}
|
|
105
|
+
return absolute;
|
|
106
|
+
}
|
|
107
|
+
export function safeRelativePath(input) {
|
|
108
|
+
if (!input || typeof input !== 'string')
|
|
109
|
+
throw new Error('relativePath is required');
|
|
110
|
+
if (path.isAbsolute(input) || input.includes('\0'))
|
|
111
|
+
throw new Error(`invalid relativePath: ${input}`);
|
|
112
|
+
const normalized = input.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
113
|
+
const resolved = path.posix.normalize(normalized);
|
|
114
|
+
if (!resolved || resolved === '.' || resolved.startsWith('../') || resolved === '..') {
|
|
115
|
+
throw new Error(`invalid relativePath: ${input}`);
|
|
116
|
+
}
|
|
117
|
+
return resolved;
|
|
118
|
+
}
|
|
119
|
+
export function parseDurationMs(value) {
|
|
120
|
+
if (!LIMIT_DURATION_RE.test(value)) {
|
|
121
|
+
throw new Error('limits.maxDuration must match ^[1-9]\\d*(s|m|h|d)$');
|
|
122
|
+
}
|
|
123
|
+
const amount = Number(value.slice(0, -1));
|
|
124
|
+
const unit = value.slice(-1);
|
|
125
|
+
const multiplier = unit === 'd' ? 86_400_000 : unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : 1_000;
|
|
126
|
+
return amount * multiplier;
|
|
127
|
+
}
|
|
128
|
+
export function renderTemplate(template, ctx) {
|
|
129
|
+
const timestamp = ctx.timestamp ?? Date.now();
|
|
130
|
+
const dateObj = new Date(timestamp);
|
|
131
|
+
const fullCtx = {
|
|
132
|
+
timestamp,
|
|
133
|
+
date: ctx.date ?? dateObj.toISOString().slice(0, 10),
|
|
134
|
+
time: ctx.time ?? dateObj.toISOString().slice(11, 19),
|
|
135
|
+
trigger: ctx.trigger,
|
|
136
|
+
execution: ctx.trigger.execution,
|
|
137
|
+
reply: ctx.reply,
|
|
138
|
+
result: ctx.result,
|
|
139
|
+
error: ctx.error,
|
|
140
|
+
event: ctx.event,
|
|
141
|
+
source: ctx.source,
|
|
142
|
+
};
|
|
143
|
+
const source = template ?? defaultTemplate(fullCtx);
|
|
144
|
+
return source.replace(/\{\{\s*([A-Za-z0-9_.]+)\s*\}\}/g, (_m, key) => {
|
|
145
|
+
const value = readPath(fullCtx, key);
|
|
146
|
+
if (value === undefined || value === null)
|
|
147
|
+
return '';
|
|
148
|
+
if (typeof value === 'string')
|
|
149
|
+
return value;
|
|
150
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
151
|
+
return String(value);
|
|
152
|
+
return JSON.stringify(value);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function defaultTemplate(ctx) {
|
|
156
|
+
const replyText = ctx.reply?.text === undefined || ctx.reply.text === null ? '' : String(ctx.reply.text);
|
|
157
|
+
if (replyText.trim())
|
|
158
|
+
return replyText;
|
|
159
|
+
const resultText = ctx.result?.text === undefined || ctx.result.text === null ? '' : String(ctx.result.text);
|
|
160
|
+
if (resultText.trim())
|
|
161
|
+
return resultText;
|
|
162
|
+
if (ctx.error?.message)
|
|
163
|
+
return ctx.error.message;
|
|
164
|
+
return '';
|
|
165
|
+
}
|
|
166
|
+
function normalizeV3Phase2(raw, opts) {
|
|
167
|
+
const now = opts.now ?? Date.now();
|
|
168
|
+
const id = optionalString(raw.id) || generateTriggerId();
|
|
169
|
+
const agentAid = requiredString(raw.agentAid, 'agentAid');
|
|
170
|
+
const name = requiredString(raw.name, 'name');
|
|
171
|
+
const createdAt = optionalNumber(raw.createdAt) ?? now;
|
|
172
|
+
const updatedAt = optionalNumber(raw.updatedAt) ?? now;
|
|
173
|
+
const definition = {
|
|
174
|
+
$schema_version: TRIGGER_SCHEMA_VERSION,
|
|
175
|
+
id,
|
|
176
|
+
agentAid,
|
|
177
|
+
enabled: raw.enabled === undefined ? true : requiredBoolean(raw.enabled, 'enabled'),
|
|
178
|
+
name,
|
|
179
|
+
description: optionalString(raw.description),
|
|
180
|
+
createdAt,
|
|
181
|
+
updatedAt,
|
|
182
|
+
origin: normalizeOrigin(raw.origin),
|
|
183
|
+
source: normalizeSource(raw.source),
|
|
184
|
+
execution: normalizeExecution(raw.execution, { id }),
|
|
185
|
+
feedback: normalizeFeedback(raw.feedback, agentAid),
|
|
186
|
+
reliability: normalizeReliability(raw.reliability),
|
|
187
|
+
limits: normalizeLimits(raw.limits),
|
|
188
|
+
};
|
|
189
|
+
validateTriggerSemantics(definition);
|
|
190
|
+
return definition;
|
|
191
|
+
}
|
|
192
|
+
function normalizeOrigin(value) {
|
|
193
|
+
if (value === undefined)
|
|
194
|
+
return undefined;
|
|
195
|
+
if (!isObject(value))
|
|
196
|
+
throw new Error('origin must be an object');
|
|
197
|
+
const raw = value;
|
|
198
|
+
const rawSession = normalizeSessionKind(raw.session, 'origin.session');
|
|
199
|
+
const threadId = optionalString(raw.threadId);
|
|
200
|
+
if (rawSession === 'main' && threadId)
|
|
201
|
+
throw new Error('origin.threadId is not allowed when origin.session=main');
|
|
202
|
+
if (rawSession === 'thread' && !threadId)
|
|
203
|
+
throw new Error('origin.threadId is required when origin.session=thread');
|
|
204
|
+
const rawChannelId = requiredString(raw.channelId, 'origin.channelId');
|
|
205
|
+
const peerId = optionalString(raw.peerId);
|
|
206
|
+
const legacyGroupOrigin = !!peerId && rawChannelId !== peerId;
|
|
207
|
+
const session = legacyGroupOrigin ? 'main' : rawSession;
|
|
208
|
+
return {
|
|
209
|
+
channelKey: requiredString(raw.channelKey, 'origin.channelKey'),
|
|
210
|
+
channelType: optionalString(raw.channelType),
|
|
211
|
+
channelId: peerId ?? rawChannelId,
|
|
212
|
+
session,
|
|
213
|
+
threadId: session === 'thread' ? threadId : undefined,
|
|
214
|
+
peerId,
|
|
215
|
+
sessionKey: legacyGroupOrigin ? undefined : optionalString(raw.sessionKey),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function normalizeSource(value) {
|
|
219
|
+
if (!isObject(value))
|
|
220
|
+
throw new Error('source must be an object');
|
|
221
|
+
const raw = value;
|
|
222
|
+
const type = requiredString(raw.type, 'source.type');
|
|
223
|
+
switch (type) {
|
|
224
|
+
case 'once':
|
|
225
|
+
rejectFields(raw, ['afterMs', 'at', 'expression', 'timezone', 'everyMs', 'eventPattern', 'filter'], 'source');
|
|
226
|
+
return { type };
|
|
227
|
+
case 'delay':
|
|
228
|
+
return { type, afterMs: positiveNumber(raw.afterMs, 'source.afterMs') };
|
|
229
|
+
case 'at': {
|
|
230
|
+
const at = requiredString(raw.at, 'source.at');
|
|
231
|
+
const ts = new Date(at).getTime();
|
|
232
|
+
if (!Number.isFinite(ts))
|
|
233
|
+
throw new Error('source.at must be a valid ISO datetime');
|
|
234
|
+
return { type, at };
|
|
235
|
+
}
|
|
236
|
+
case 'cron':
|
|
237
|
+
return {
|
|
238
|
+
type,
|
|
239
|
+
expression: requiredString(raw.expression, 'source.expression'),
|
|
240
|
+
timezone: optionalString(raw.timezone),
|
|
241
|
+
};
|
|
242
|
+
case 'interval':
|
|
243
|
+
return { type, everyMs: positiveNumber(raw.everyMs, 'source.everyMs') };
|
|
244
|
+
case 'event': {
|
|
245
|
+
const eventPattern = requiredString(raw.eventPattern, 'source.eventPattern');
|
|
246
|
+
validateEventPattern(eventPattern);
|
|
247
|
+
const filter = normalizeEventFilter(raw.filter);
|
|
248
|
+
return filter ? { type, eventPattern, filter } : { type, eventPattern };
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
throw new Error(`unsupported source.type: ${type}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function validateEventPattern(pattern) {
|
|
255
|
+
if (pattern === '*')
|
|
256
|
+
return;
|
|
257
|
+
if (!/^[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$/.test(pattern) && !/^[A-Za-z0-9_-]+:\*$/.test(pattern)) {
|
|
258
|
+
throw new Error('source.eventPattern must be "*", an exact event name, or a prefix pattern like "message:*"');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function normalizeEventFilter(value) {
|
|
262
|
+
if (value === undefined)
|
|
263
|
+
return undefined;
|
|
264
|
+
if (!isObject(value))
|
|
265
|
+
throw new Error('source.filter must be an object');
|
|
266
|
+
const raw = value;
|
|
267
|
+
if ('where' in raw)
|
|
268
|
+
throw new Error('source.filter.where is not supported in MVP');
|
|
269
|
+
const allowed = new Set(['match']);
|
|
270
|
+
for (const key of Object.keys(raw)) {
|
|
271
|
+
if (!allowed.has(key))
|
|
272
|
+
throw new Error(`unsupported source.filter.${key}`);
|
|
273
|
+
}
|
|
274
|
+
if (raw.match === undefined)
|
|
275
|
+
return undefined;
|
|
276
|
+
if (!isObject(raw.match))
|
|
277
|
+
throw new Error('source.filter.match must be an object');
|
|
278
|
+
const match = {};
|
|
279
|
+
for (const [pathKey, expected] of Object.entries(raw.match)) {
|
|
280
|
+
validateFilterPath(pathKey);
|
|
281
|
+
match[pathKey] = normalizeMatchValue(expected, `source.filter.match.${pathKey}`);
|
|
282
|
+
}
|
|
283
|
+
return { match };
|
|
284
|
+
}
|
|
285
|
+
function validateFilterPath(pathKey) {
|
|
286
|
+
if (!pathKey || typeof pathKey !== 'string')
|
|
287
|
+
throw new Error('source.filter.match path must be a non-empty string');
|
|
288
|
+
for (const part of pathKey.split('.')) {
|
|
289
|
+
if (!part)
|
|
290
|
+
throw new Error(`source.filter.match path is invalid: ${pathKey}`);
|
|
291
|
+
if (part === '__proto__' || part === 'prototype' || part === 'constructor') {
|
|
292
|
+
throw new Error(`source.filter.match path is not allowed: ${pathKey}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function normalizeMatchValue(value, label) {
|
|
297
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
|
|
298
|
+
return value;
|
|
299
|
+
if (!isObject(value))
|
|
300
|
+
throw new Error(`${label} must be a scalar or match operator object`);
|
|
301
|
+
const raw = value;
|
|
302
|
+
const keys = Object.keys(raw);
|
|
303
|
+
if (keys.length === 0)
|
|
304
|
+
throw new Error(`${label} must not be empty`);
|
|
305
|
+
const normalized = {};
|
|
306
|
+
for (const key of keys) {
|
|
307
|
+
switch (key) {
|
|
308
|
+
case '$in':
|
|
309
|
+
if (!Array.isArray(raw[key]))
|
|
310
|
+
throw new Error(`${label}.$in must be an array`);
|
|
311
|
+
normalized[key] = raw[key];
|
|
312
|
+
break;
|
|
313
|
+
case '$regex':
|
|
314
|
+
if (typeof raw[key] !== 'string')
|
|
315
|
+
throw new Error(`${label}.$regex must be a string`);
|
|
316
|
+
if (raw[key].length > 512)
|
|
317
|
+
throw new Error(`${label}.$regex is too long`);
|
|
318
|
+
try {
|
|
319
|
+
new RegExp(raw[key]);
|
|
320
|
+
}
|
|
321
|
+
catch (err) {
|
|
322
|
+
throw new Error(`${label}.$regex is invalid: ${err?.message || String(err)}`);
|
|
323
|
+
}
|
|
324
|
+
normalized[key] = raw[key];
|
|
325
|
+
break;
|
|
326
|
+
case '$gt':
|
|
327
|
+
case '$gte':
|
|
328
|
+
case '$lt':
|
|
329
|
+
case '$lte':
|
|
330
|
+
if (typeof raw[key] !== 'number' || !Number.isFinite(raw[key]))
|
|
331
|
+
throw new Error(`${label}.${key} must be a finite number`);
|
|
332
|
+
normalized[key] = raw[key];
|
|
333
|
+
break;
|
|
334
|
+
case '$exists':
|
|
335
|
+
if (typeof raw[key] !== 'boolean')
|
|
336
|
+
throw new Error(`${label}.$exists must be boolean`);
|
|
337
|
+
normalized[key] = raw[key];
|
|
338
|
+
break;
|
|
339
|
+
default:
|
|
340
|
+
throw new Error(`unsupported match operator: ${key}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return normalized;
|
|
344
|
+
}
|
|
345
|
+
function normalizeExecution(value, _opts) {
|
|
346
|
+
if (!isObject(value))
|
|
347
|
+
throw new Error('execution must be an object');
|
|
348
|
+
const raw = value;
|
|
349
|
+
if ('mode' in raw)
|
|
350
|
+
throw new Error('execution.mode is not supported in Trigger V3 phase 2; use execution.type');
|
|
351
|
+
if ('session' in raw)
|
|
352
|
+
throw new Error('execution.session is not supported in Trigger V3 phase 2; use execution.thread or feedback.target');
|
|
353
|
+
const type = requiredString(raw.type, 'execution.type');
|
|
354
|
+
if (type !== 'script' && type !== 'trigger_session' && type !== 'target_session') {
|
|
355
|
+
throw new Error('execution.type must be script, trigger_session, or target_session');
|
|
356
|
+
}
|
|
357
|
+
const thread = normalizeExecutionThread(raw.thread);
|
|
358
|
+
const execution = {
|
|
359
|
+
type,
|
|
360
|
+
prompt: type === 'script' ? optionalString(raw.prompt) : requiredString(raw.prompt, 'execution.prompt'),
|
|
361
|
+
script: type === 'script' ? normalizeScript(raw.script) : undefined,
|
|
362
|
+
thread: type === 'trigger_session' ? (thread ?? 'by_trigger') : thread,
|
|
363
|
+
baseagent: normalizeExecutionBaseagent(raw.baseagent),
|
|
364
|
+
model: optionalString(raw.model),
|
|
365
|
+
effort: normalizeEffort(raw.effort),
|
|
366
|
+
permissionMode: normalizeTriggerPermissionMode(raw.permissionMode),
|
|
367
|
+
onError: normalizeOnError(raw.onError),
|
|
368
|
+
noopSentinel: optionalString(raw.noopSentinel) ?? DEFAULT_NOOP_SENTINEL,
|
|
369
|
+
};
|
|
370
|
+
return execution;
|
|
371
|
+
}
|
|
372
|
+
function normalizeExecutionBaseagent(value) {
|
|
373
|
+
if (value === undefined)
|
|
374
|
+
return undefined;
|
|
375
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
376
|
+
throw new Error('execution.baseagent must be a non-empty string');
|
|
377
|
+
}
|
|
378
|
+
return value.trim();
|
|
379
|
+
}
|
|
380
|
+
function normalizeExecutionThread(value) {
|
|
381
|
+
const thread = optionalString(value);
|
|
382
|
+
if (thread === undefined)
|
|
383
|
+
return undefined;
|
|
384
|
+
if (thread !== 'per_run' && thread !== 'by_trigger') {
|
|
385
|
+
throw new Error('execution.thread must be per_run or by_trigger');
|
|
386
|
+
}
|
|
387
|
+
return thread;
|
|
388
|
+
}
|
|
389
|
+
function normalizeEffort(value) {
|
|
390
|
+
const effort = optionalString(value);
|
|
391
|
+
if (effort === undefined)
|
|
392
|
+
return undefined;
|
|
393
|
+
if (!TRIGGER_EFFORTS.has(effort)) {
|
|
394
|
+
throw new Error('execution.effort must be one of low, medium, high, xhigh, max');
|
|
395
|
+
}
|
|
396
|
+
return effort;
|
|
397
|
+
}
|
|
398
|
+
function normalizeTriggerPermissionMode(value) {
|
|
399
|
+
const mode = optionalString(value);
|
|
400
|
+
if (mode === undefined)
|
|
401
|
+
return undefined;
|
|
402
|
+
if (PERMISSION_MODES.has(mode)) {
|
|
403
|
+
return mode;
|
|
404
|
+
}
|
|
405
|
+
// Persisted Trigger V3 phase 2 definitions may still contain legacy values. Normalize those at
|
|
406
|
+
// load time while new CLI/menu input only accepts the four public modes.
|
|
407
|
+
if (mode === 'edit' || mode === 'noask' || mode === 'plan') {
|
|
408
|
+
return normalizePermissionModeContract(mode).mode;
|
|
409
|
+
}
|
|
410
|
+
throw new Error('execution.permissionMode must be one of readonly, auto, request, bypass');
|
|
411
|
+
}
|
|
412
|
+
function normalizeScript(value) {
|
|
413
|
+
if (!isObject(value))
|
|
414
|
+
throw new Error('execution.script must be an object');
|
|
415
|
+
const raw = value;
|
|
416
|
+
const command = splitScriptCommand(requiredString(raw.path, 'script.path'));
|
|
417
|
+
if (!command.path)
|
|
418
|
+
throw new Error('script.path is required');
|
|
419
|
+
const timeoutMs = optionalNumber(raw.timeoutMs) ?? 30_000;
|
|
420
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_SCRIPT_TIMEOUT_MS) {
|
|
421
|
+
throw new Error(`script.timeoutMs must be between 1 and ${MAX_SCRIPT_TIMEOUT_MS}`);
|
|
422
|
+
}
|
|
423
|
+
const args = raw.args !== undefined
|
|
424
|
+
? raw.args
|
|
425
|
+
: (command.args.length > 0 ? command.args : undefined);
|
|
426
|
+
return {
|
|
427
|
+
path: command.path,
|
|
428
|
+
runtime: requiredString(raw.runtime, 'script.runtime'),
|
|
429
|
+
args,
|
|
430
|
+
timeoutMs,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function normalizeFeedback(value, agentAid) {
|
|
434
|
+
if (!isObject(value))
|
|
435
|
+
throw new Error('feedback must be an object');
|
|
436
|
+
const raw = value;
|
|
437
|
+
const strategy = requiredString(raw.strategy, 'feedback.strategy');
|
|
438
|
+
if (strategy !== 'origin' && strategy !== 'target' && strategy !== 'silent') {
|
|
439
|
+
throw new Error('feedback.strategy must be origin, target, or silent');
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
strategy,
|
|
443
|
+
target: raw.target === undefined ? undefined : normalizeFeedbackTarget(raw.target, 'feedback.target', agentAid),
|
|
444
|
+
onReply: normalizeFeedbackTemplate(raw.onReply, 'feedback.onReply'),
|
|
445
|
+
onNoop: normalizeFeedbackTemplate(raw.onNoop, 'feedback.onNoop'),
|
|
446
|
+
onFailure: normalizeFeedbackTemplate(raw.onFailure, 'feedback.onFailure'),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function normalizeFeedbackTarget(value, label, agentAid) {
|
|
450
|
+
if (!isObject(value))
|
|
451
|
+
throw new Error(`${label} must be an object`);
|
|
452
|
+
const raw = value;
|
|
453
|
+
const session = normalizeSessionKind(raw.session, `${label}.session`);
|
|
454
|
+
const threadId = optionalString(raw.threadId);
|
|
455
|
+
if (session === 'main' && threadId)
|
|
456
|
+
throw new Error(`${label}.threadId is not allowed when ${label}.session=main`);
|
|
457
|
+
if (session === 'thread' && !threadId)
|
|
458
|
+
throw new Error(`${label}.threadId is required when ${label}.session=thread`);
|
|
459
|
+
const channelKey = requiredString(raw.channelKey, `${label}.channelKey`);
|
|
460
|
+
return {
|
|
461
|
+
channelKey: channelKey === 'aun'
|
|
462
|
+
? formatChannelKey({ type: 'aun', selfAID: agentAid, name: 'main' })
|
|
463
|
+
: channelKey,
|
|
464
|
+
channelId: requiredString(raw.channelId, `${label}.channelId`),
|
|
465
|
+
session,
|
|
466
|
+
threadId,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function normalizeFeedbackTemplate(value, label) {
|
|
470
|
+
if (value === undefined)
|
|
471
|
+
return undefined;
|
|
472
|
+
if (!isObject(value))
|
|
473
|
+
throw new Error(`${label} must be an object`);
|
|
474
|
+
const raw = value;
|
|
475
|
+
return { template: optionalTemplate(raw.template) };
|
|
476
|
+
}
|
|
477
|
+
function normalizeSessionKind(value, label) {
|
|
478
|
+
const session = optionalString(value) ?? 'main';
|
|
479
|
+
if (session !== 'main' && session !== 'thread')
|
|
480
|
+
throw new Error(`${label} must be main or thread`);
|
|
481
|
+
return session;
|
|
482
|
+
}
|
|
483
|
+
function normalizeReliability(value) {
|
|
484
|
+
if (value === undefined) {
|
|
485
|
+
return { concurrency: 'forbid', missedPolicy: 'run_once', retry: { maxAttempts: 0, backoffMs: 30_000 } };
|
|
486
|
+
}
|
|
487
|
+
if (!isObject(value))
|
|
488
|
+
throw new Error('reliability must be an object');
|
|
489
|
+
const raw = value;
|
|
490
|
+
const concurrency = optionalString(raw.concurrency) ?? 'forbid';
|
|
491
|
+
if (concurrency !== 'forbid' && concurrency !== 'replace' && concurrency !== 'allow') {
|
|
492
|
+
throw new Error('reliability.concurrency is invalid');
|
|
493
|
+
}
|
|
494
|
+
const missedPolicy = optionalString(raw.missedPolicy) ?? 'run_once';
|
|
495
|
+
if (missedPolicy !== 'skip' && missedPolicy !== 'run_once' && missedPolicy !== 'run_all') {
|
|
496
|
+
throw new Error('reliability.missedPolicy is invalid');
|
|
497
|
+
}
|
|
498
|
+
const retryRaw = isObject(raw.retry) ? raw.retry : {};
|
|
499
|
+
const maxAttempts = optionalNumber(retryRaw.maxAttempts) ?? 0;
|
|
500
|
+
const backoffMs = optionalNumber(retryRaw.backoffMs) ?? 30_000;
|
|
501
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 0 || maxAttempts > 20) {
|
|
502
|
+
throw new Error('reliability.retry.maxAttempts must be an integer between 0 and 20');
|
|
503
|
+
}
|
|
504
|
+
if (!Number.isFinite(backoffMs) || backoffMs < 0) {
|
|
505
|
+
throw new Error('reliability.retry.backoffMs must be a non-negative number');
|
|
506
|
+
}
|
|
507
|
+
return { concurrency, missedPolicy, retry: { maxAttempts, backoffMs } };
|
|
508
|
+
}
|
|
509
|
+
function normalizeLimits(value) {
|
|
510
|
+
if (value === undefined)
|
|
511
|
+
return undefined;
|
|
512
|
+
if (!isObject(value))
|
|
513
|
+
throw new Error('limits must be an object');
|
|
514
|
+
const raw = value;
|
|
515
|
+
const limits = {};
|
|
516
|
+
if (raw.maxRuns !== undefined) {
|
|
517
|
+
const maxRuns = optionalNumber(raw.maxRuns);
|
|
518
|
+
if (maxRuns === undefined || !Number.isInteger(maxRuns) || maxRuns <= 0) {
|
|
519
|
+
throw new Error('limits.maxRuns must be a positive integer');
|
|
520
|
+
}
|
|
521
|
+
limits.maxRuns = maxRuns;
|
|
522
|
+
}
|
|
523
|
+
if (raw.maxDuration !== undefined) {
|
|
524
|
+
const maxDuration = requiredString(raw.maxDuration, 'limits.maxDuration');
|
|
525
|
+
parseDurationMs(maxDuration);
|
|
526
|
+
limits.maxDuration = maxDuration;
|
|
527
|
+
}
|
|
528
|
+
return limits.maxRuns === undefined && limits.maxDuration === undefined ? undefined : limits;
|
|
529
|
+
}
|
|
530
|
+
function normalizeOnError(value) {
|
|
531
|
+
const onError = optionalString(value) ?? 'fail';
|
|
532
|
+
if (onError !== 'fail' && onError !== 'retry')
|
|
533
|
+
throw new Error('execution.onError must be fail or retry');
|
|
534
|
+
return onError;
|
|
535
|
+
}
|
|
536
|
+
function validateTriggerSemantics(definition) {
|
|
537
|
+
const execution = definition.execution;
|
|
538
|
+
if (execution.type === 'script') {
|
|
539
|
+
if (!execution.script)
|
|
540
|
+
throw new Error('execution.script is required when execution.type=script');
|
|
541
|
+
if (execution.prompt !== undefined)
|
|
542
|
+
throw new Error('execution.prompt is not allowed when execution.type=script');
|
|
543
|
+
if (execution.thread !== undefined)
|
|
544
|
+
throw new Error('execution.thread is not allowed when execution.type=script');
|
|
545
|
+
if (execution.baseagent !== undefined)
|
|
546
|
+
throw new Error('execution.baseagent is only allowed when execution.type=trigger_session');
|
|
547
|
+
}
|
|
548
|
+
if (execution.type === 'trigger_session') {
|
|
549
|
+
if (!execution.prompt)
|
|
550
|
+
throw new Error('execution.prompt is required when execution.type=trigger_session');
|
|
551
|
+
if (execution.script !== undefined)
|
|
552
|
+
throw new Error('execution.script is not allowed when execution.type=trigger_session');
|
|
553
|
+
}
|
|
554
|
+
if (execution.type === 'target_session') {
|
|
555
|
+
if (!execution.prompt)
|
|
556
|
+
throw new Error('execution.prompt is required when execution.type=target_session');
|
|
557
|
+
if (execution.script !== undefined)
|
|
558
|
+
throw new Error('execution.script is not allowed when execution.type=target_session');
|
|
559
|
+
if (execution.thread !== undefined)
|
|
560
|
+
throw new Error('execution.thread is not allowed when execution.type=target_session');
|
|
561
|
+
if (execution.baseagent !== undefined)
|
|
562
|
+
throw new Error('execution.baseagent is only allowed when execution.type=trigger_session');
|
|
563
|
+
if (definition.feedback.strategy === 'silent')
|
|
564
|
+
throw new Error('feedback.strategy=silent is not allowed when execution.type=target_session');
|
|
565
|
+
}
|
|
566
|
+
if (definition.feedback.strategy === 'target' && !definition.feedback.target) {
|
|
567
|
+
throw new Error('feedback.target is required when feedback.strategy=target');
|
|
568
|
+
}
|
|
569
|
+
if (definition.feedback.strategy !== 'target' && definition.feedback.target) {
|
|
570
|
+
throw new Error('feedback.target is only allowed when feedback.strategy=target');
|
|
571
|
+
}
|
|
572
|
+
if (execution.type !== 'script' && (definition.feedback.onReply || definition.feedback.onNoop || definition.feedback.onFailure)) {
|
|
573
|
+
throw new Error('feedback.onReply/onNoop/onFailure are only allowed when execution.type=script');
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function generateTriggerId() {
|
|
577
|
+
return `trig_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
578
|
+
}
|
|
579
|
+
function requiredString(value, label) {
|
|
580
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
581
|
+
throw new Error(`${label} is required`);
|
|
582
|
+
return value;
|
|
583
|
+
}
|
|
584
|
+
function optionalString(value) {
|
|
585
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
586
|
+
}
|
|
587
|
+
function optionalTemplate(value) {
|
|
588
|
+
if (value === undefined || value === null)
|
|
589
|
+
return undefined;
|
|
590
|
+
if (typeof value !== 'string')
|
|
591
|
+
throw new Error('feedback template must be a string when provided');
|
|
592
|
+
return value.trim() ? value : undefined;
|
|
593
|
+
}
|
|
594
|
+
function optionalNumber(value) {
|
|
595
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
596
|
+
}
|
|
597
|
+
function positiveNumber(value, label) {
|
|
598
|
+
const n = optionalNumber(value);
|
|
599
|
+
if (n === undefined || n <= 0)
|
|
600
|
+
throw new Error(`${label} must be a positive number`);
|
|
601
|
+
return n;
|
|
602
|
+
}
|
|
603
|
+
function rejectFields(raw, fields, label) {
|
|
604
|
+
for (const field of fields) {
|
|
605
|
+
if (raw[field] !== undefined)
|
|
606
|
+
throw new Error(`${label}.${field} is not allowed`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function requiredBoolean(value, label) {
|
|
610
|
+
if (typeof value !== 'boolean')
|
|
611
|
+
throw new Error(`${label} must be boolean`);
|
|
612
|
+
return value;
|
|
613
|
+
}
|
|
614
|
+
function isObject(value) {
|
|
615
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
616
|
+
}
|
|
617
|
+
function readPath(root, key) {
|
|
618
|
+
let cur = root;
|
|
619
|
+
for (const part of key.split('.')) {
|
|
620
|
+
if (cur == null || typeof cur !== 'object')
|
|
621
|
+
return undefined;
|
|
622
|
+
cur = cur[part];
|
|
623
|
+
}
|
|
624
|
+
return cur;
|
|
625
|
+
}
|
|
626
|
+
function stableStringify(value) {
|
|
627
|
+
if (Array.isArray(value))
|
|
628
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
629
|
+
if (value && typeof value === 'object') {
|
|
630
|
+
const obj = value;
|
|
631
|
+
return `{${Object.keys(obj).sort().map(k => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(',')}}`;
|
|
632
|
+
}
|
|
633
|
+
return JSON.stringify(value);
|
|
634
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const DEFAULT_FLUSH_DELAY_SECONDS = 3;
|
|
2
|
+
export const DEFAULT_FLUSH_DELAY_MS = DEFAULT_FLUSH_DELAY_SECONDS * 1000;
|
|
3
|
+
/** Fail-closed runner fallback when no role/relation policy reaches the call. */
|
|
4
|
+
export const DEFAULT_PERMISSION_MODE = 'readonly';
|
|
5
|
+
/** Placeholder agent name for legacy code paths that haven't been migrated. */
|
|
6
|
+
export const DEFAULT_AGENT_NAME = '<unknown>';
|
|
7
|
+
// ── 新结构:defaults.json + per-agent config.json(evolcore-home-directory.md)──
|
|
8
|
+
//
|
|
9
|
+
// 旧 Config / EvolAgentConfig 保留至阶段 3 退场。新结构的承载是
|
|
10
|
+
// DefaultsConfig(agents/defaults.json)+ AgentConfig(agents/<aid>/config.json),
|
|
11
|
+
// 由 ConfigStore 加载并算 effective merged 值。
|
|
12
|
+
export const CONFIG_SCHEMA_VERSION = 2;
|