evolcore 0.0.21 → 0.0.23
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/AGENTS.md +62 -0
- package/CHANGELOG.md +53 -0
- package/README.md +2 -0
- package/bin/codex-managed-acl.ps1 +155 -0
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +376 -96
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +1 -1
- package/dist/agents/claude-runner.js +44 -226
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +103 -17
- package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
- package/dist/agents/codex/catalog.js +70 -0
- package/dist/agents/codex/diagnostics.js +154 -0
- package/dist/agents/codex-runner.js +1292 -219
- package/dist/agents/ecagent-runner.js +10 -31
- package/dist/agents/gemini-runner.js +2 -6
- package/dist/aun/aid/store.js +2 -0
- package/dist/aun/menu-client.js +272 -0
- package/dist/aun/msg/group.js +65 -4
- package/dist/aun/msg/index.js +1 -1
- package/dist/aun/msg/managed-operation.js +51 -4
- package/dist/aun/outbox.js +7 -0
- package/dist/aun/rpc/client.js +8 -1
- package/dist/channels/aun.js +240 -36
- package/dist/channels/wechat.js +81 -10
- package/dist/cli/aun-commands.js +48 -4
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/daemon-commands.js +169 -51
- package/dist/cli/index.js +20 -0
- package/dist/cli/init.js +2 -10
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/task-context.js +16 -2
- package/dist/cli/trigger-command.js +22 -5
- package/dist/config/config-field-policy.js +3 -0
- package/dist/config/config-manager.js +32 -5
- package/dist/config/contact-book-store.js +25 -3
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +15 -0
- package/dist/core/auth/auth-gateway.js +8 -0
- package/dist/core/auth/authorization-audit.js +70 -6
- package/dist/core/auth/operation-catalog.js +4 -0
- package/dist/core/auth/remote-access-store.js +135 -0
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +93 -25
- package/dist/core/command/command-handler.js +62 -5
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +150 -33
- package/dist/core/command/menu-protocol.js +120 -1
- package/dist/core/command/slash-gate.js +3 -3
- package/dist/core/command/slash-handler.js +83 -40
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +32 -0
- package/dist/core/handoff/runtime.js +23 -3
- package/dist/core/message/im-renderer.js +7 -3
- package/dist/core/message/message-bridge.js +60 -2
- package/dist/core/message/message-log.js +33 -0
- package/dist/core/message/message-queue.js +41 -15
- package/dist/core/message/response-engine.js +246 -114
- package/dist/core/model/catalog-service.js +308 -0
- package/dist/core/model/model-catalog.js +124 -334
- package/dist/core/model/model-diagnostics.js +4 -5
- package/dist/core/model/model-fallback.js +9 -16
- package/dist/core/model/model-permission.js +1 -1
- package/dist/core/permission/approval-gateway.js +16 -1
- package/dist/core/permission/ec-command-parser.js +272 -70
- package/dist/core/permission/protected-paths.js +74 -14
- package/dist/core/permission/sandbox-runtime.js +4 -2
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +290 -30
- package/dist/core/runtime-lock.js +13 -8
- package/dist/core/session/session-manager.js +30 -0
- package/dist/core/session/session-renew.js +18 -1
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/index.js +104 -34
- package/dist/ipc.js +218 -96
- package/dist/paths.js +18 -0
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/feedback.js +14 -2
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +86 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/trigger/scheduler.js +20 -3
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +7 -0
- package/dist/utils/logger.js +9 -4
- package/dist/utils/npm-ops.js +47 -15
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/evolcore/INDEX.md +3 -2
- package/kits/docs/evolcore/agent-create.md +159 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/group-collaboration.md +256 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/menu-client.md +34 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
- package/kits/migrations/migrate-role-config-v4.mjs +11 -5
- package/kits/migrations/migrate-role-config-v5.mjs +8 -2
- package/kits/schemas/_meta.json +3 -2
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +10 -2
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +11 -3
package/dist/trigger/parser.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { CronExpressionParser } from 'cron-parser';
|
|
2
2
|
const TRIGGER_UPDATE_FLAGS = new Set([
|
|
3
3
|
'once', 'delay', 'at', 'cron', 'every', 'event', 'tz',
|
|
4
|
-
'prompt', 'name',
|
|
4
|
+
'prompt', 'name', 'description',
|
|
5
|
+
'script-file', 'script-runtime', 'script-args', 'script-timeout',
|
|
6
|
+
'feedback', 'target-channel', 'target-channel-id', 'target-chat-type', 'target-session', 'target-thread-id',
|
|
7
|
+
'trigger-thread', 'baseagent',
|
|
8
|
+
'on-error', 'noop-sentinel', 'retry-attempts', 'retry-backoff',
|
|
5
9
|
'model', 'effort', 'permission',
|
|
6
10
|
'max-runs', 'max-duration', 'concurrency', 'missed-policy',
|
|
7
11
|
]);
|
|
@@ -395,18 +399,22 @@ function commonParsed(flags, opts = {}) {
|
|
|
395
399
|
if (!opts.update && !execution.value) {
|
|
396
400
|
return { ok: false, error: '创建 trigger 必须显式指定 --exec script|trigger-session|target-session' };
|
|
397
401
|
}
|
|
402
|
+
// update 场景下执行类型未知(不重命名 --exec),execution-type 相关校验
|
|
403
|
+
// 全部交给 patch/validation 按现有定义执行。
|
|
398
404
|
const inferredExecution = execution.value;
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
405
|
+
if (!opts.update) {
|
|
406
|
+
if (baseagent.value && inferredExecution !== 'trigger_session') {
|
|
407
|
+
return { ok: false, error: '--baseagent 仅适用于 --exec trigger-session' };
|
|
408
|
+
}
|
|
409
|
+
if (inferredExecution === 'script') {
|
|
410
|
+
if (!scriptPath)
|
|
411
|
+
return { ok: false, error: '--exec script 需要 --script-path' };
|
|
412
|
+
if (scriptPath && !scriptRuntime)
|
|
413
|
+
return { ok: false, error: '--script-path 必须配合 --script-runtime 使用(node|python|bash)' };
|
|
414
|
+
}
|
|
415
|
+
else if (!prompt) {
|
|
416
|
+
return { ok: false, error: '--prompt 为必填项' };
|
|
417
|
+
}
|
|
410
418
|
}
|
|
411
419
|
const targetSessionValue = targetSession.value ?? (targetThreadId ? 'thread' : 'main');
|
|
412
420
|
const feedbackStrategy = feedback.value ?? (hasTargetField ? 'target' : 'origin');
|
|
@@ -552,7 +560,7 @@ export function parseTriggerUpdateArgv(nameOrId, args, opts = {}) {
|
|
|
552
560
|
const parsedFlags = flagsFromArgv(args, 'update');
|
|
553
561
|
if (!parsedFlags.ok)
|
|
554
562
|
return { ok: false, error: withPromptFileHint(parsedFlags.error, args, opts) };
|
|
555
|
-
const parsed = parseTriggerUpdateFlags(nameOrId, parsedFlags.flags, opts);
|
|
563
|
+
const parsed = parseTriggerUpdateFlags(nameOrId, parsedFlags.flags, { ...opts, allowScriptFile: true });
|
|
556
564
|
return parsed.ok ? parsed : { ok: false, error: withPromptFileHint(parsed.error, args, opts) };
|
|
557
565
|
}
|
|
558
566
|
function flagsFromArgv(args, command) {
|
|
@@ -585,6 +593,9 @@ function parseTriggerUpdateFlags(nameOrId, flags, opts = {}) {
|
|
|
585
593
|
if (!TRIGGER_UPDATE_FLAGS.has(flag))
|
|
586
594
|
return { ok: false, error: `update 不支持参数 --${flag}` };
|
|
587
595
|
}
|
|
596
|
+
if (flags.has('script-file') && !opts.allowScriptFile) {
|
|
597
|
+
return { ok: false, error: '--script-file 仅支持 ec trigger update CLI;请通过受控 CLI 上传脚本文件' };
|
|
598
|
+
}
|
|
588
599
|
const parsed = commonParsed(flags, { update: true, ...opts });
|
|
589
600
|
if (!parsed.ok)
|
|
590
601
|
return parsed;
|
|
@@ -607,6 +618,11 @@ function parseTriggerUpdateFlags(nameOrId, flags, opts = {}) {
|
|
|
607
618
|
update.name = value.name;
|
|
608
619
|
if (value.prompt !== undefined)
|
|
609
620
|
update.prompt = value.prompt;
|
|
621
|
+
const scriptFile = optionalFlagString(flags, 'script-file');
|
|
622
|
+
if ('error' in scriptFile)
|
|
623
|
+
return { ok: false, error: scriptFile.error };
|
|
624
|
+
if (scriptFile.value !== undefined)
|
|
625
|
+
update.scriptFile = scriptFile.value;
|
|
610
626
|
if (value.model !== undefined)
|
|
611
627
|
update.model = value.model;
|
|
612
628
|
if (value.effort !== undefined)
|
|
@@ -636,6 +652,63 @@ function parseTriggerUpdateFlags(nameOrId, flags, opts = {}) {
|
|
|
636
652
|
}
|
|
637
653
|
update.missedPolicy = normalized;
|
|
638
654
|
}
|
|
655
|
+
const description = optionalFlagString(flags, 'description');
|
|
656
|
+
if ('error' in description)
|
|
657
|
+
return { ok: false, error: description.error };
|
|
658
|
+
if (description.value !== undefined)
|
|
659
|
+
update.description = description.value || null;
|
|
660
|
+
if (flags.has('feedback') && value.feedbackStrategy !== undefined)
|
|
661
|
+
update.feedback = value.feedbackStrategy;
|
|
662
|
+
if (value.targetChannel !== undefined)
|
|
663
|
+
update.targetChannel = value.targetChannel;
|
|
664
|
+
if (value.targetChannelId !== undefined)
|
|
665
|
+
update.targetChannelId = value.targetChannelId;
|
|
666
|
+
if (value.targetChatType !== undefined)
|
|
667
|
+
update.targetChatType = value.targetChatType;
|
|
668
|
+
if (value.targetSession !== undefined && flags.has('target-session'))
|
|
669
|
+
update.targetSession = value.targetSession;
|
|
670
|
+
if (value.targetThreadId !== undefined)
|
|
671
|
+
update.targetThreadId = value.targetThreadId;
|
|
672
|
+
if (value.triggerThread !== undefined)
|
|
673
|
+
update.triggerThread = value.triggerThread;
|
|
674
|
+
if (value.baseagent !== undefined)
|
|
675
|
+
update.baseagent = value.baseagent;
|
|
676
|
+
const onError = optionalFlagString(flags, 'on-error');
|
|
677
|
+
if ('error' in onError)
|
|
678
|
+
return { ok: false, error: onError.error };
|
|
679
|
+
if (onError.value !== undefined) {
|
|
680
|
+
if (onError.value !== 'fail' && onError.value !== 'retry') {
|
|
681
|
+
return { ok: false, error: '--on-error 只接受 fail 或 retry' };
|
|
682
|
+
}
|
|
683
|
+
update.onError = onError.value;
|
|
684
|
+
}
|
|
685
|
+
const noopSentinel = optionalFlagString(flags, 'noop-sentinel');
|
|
686
|
+
if ('error' in noopSentinel)
|
|
687
|
+
return { ok: false, error: noopSentinel.error };
|
|
688
|
+
if (noopSentinel.value !== undefined)
|
|
689
|
+
update.noopSentinel = noopSentinel.value || null;
|
|
690
|
+
const retryAttempts = optionalFlagString(flags, 'retry-attempts');
|
|
691
|
+
if ('error' in retryAttempts)
|
|
692
|
+
return { ok: false, error: retryAttempts.error };
|
|
693
|
+
if (retryAttempts.value !== undefined) {
|
|
694
|
+
if (!/^\d+$/.test(retryAttempts.value))
|
|
695
|
+
return { ok: false, error: '--retry-attempts 必须是非负整数' };
|
|
696
|
+
update.retryAttempts = Number(retryAttempts.value);
|
|
697
|
+
}
|
|
698
|
+
const retryBackoff = optionalFlagString(flags, 'retry-backoff');
|
|
699
|
+
if ('error' in retryBackoff)
|
|
700
|
+
return { ok: false, error: retryBackoff.error };
|
|
701
|
+
if (retryBackoff.value !== undefined) {
|
|
702
|
+
if (!/^\d+$/.test(retryBackoff.value))
|
|
703
|
+
return { ok: false, error: '--retry-backoff 必须是毫秒正整数' };
|
|
704
|
+
update.retryBackoff = Number(retryBackoff.value);
|
|
705
|
+
}
|
|
706
|
+
if (value.scriptRuntime !== undefined)
|
|
707
|
+
update.scriptRuntime = value.scriptRuntime;
|
|
708
|
+
if (value.scriptArgs !== undefined)
|
|
709
|
+
update.scriptArgs = value.scriptArgs;
|
|
710
|
+
if (value.scriptTimeoutMs !== undefined)
|
|
711
|
+
update.scriptTimeout = value.scriptTimeoutMs;
|
|
639
712
|
if (Object.keys(update).length === 0)
|
|
640
713
|
return { ok: false, error: '没有可更新的 trigger 参数' };
|
|
641
714
|
return { ok: true, nameOrId, value: update };
|
package/dist/trigger/patch.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeTriggerDefinition, parseDurationMs, TRIGGER_SCHEMA_VERSION } from './validation.js';
|
|
2
2
|
const PATCH_KEYS = new Set([
|
|
3
3
|
'name',
|
|
4
|
+
'description',
|
|
4
5
|
'scheduleType',
|
|
5
6
|
'scheduleValue',
|
|
6
7
|
'timezone',
|
|
@@ -12,6 +13,21 @@ const PATCH_KEYS = new Set([
|
|
|
12
13
|
'maxDuration',
|
|
13
14
|
'concurrency',
|
|
14
15
|
'missedPolicy',
|
|
16
|
+
'feedback',
|
|
17
|
+
'targetChannel',
|
|
18
|
+
'targetChannelId',
|
|
19
|
+
'targetChatType',
|
|
20
|
+
'targetSession',
|
|
21
|
+
'targetThreadId',
|
|
22
|
+
'baseagent',
|
|
23
|
+
'triggerThread',
|
|
24
|
+
'onError',
|
|
25
|
+
'noopSentinel',
|
|
26
|
+
'retryAttempts',
|
|
27
|
+
'retryBackoff',
|
|
28
|
+
'scriptRuntime',
|
|
29
|
+
'scriptArgs',
|
|
30
|
+
'scriptTimeout',
|
|
15
31
|
]);
|
|
16
32
|
export function applyTriggerPatch(definition, input, opts = {}) {
|
|
17
33
|
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
@@ -48,6 +64,12 @@ export function applyTriggerPatch(definition, input, opts = {}) {
|
|
|
48
64
|
updated.reliability.concurrency = patch.concurrency;
|
|
49
65
|
if (patch.missedPolicy !== undefined)
|
|
50
66
|
updated.reliability.missedPolicy = patch.missedPolicy;
|
|
67
|
+
if (patch.description !== undefined) {
|
|
68
|
+
if (patch.description === null || patch.description === '')
|
|
69
|
+
delete updated.description;
|
|
70
|
+
else
|
|
71
|
+
updated.description = String(patch.description);
|
|
72
|
+
}
|
|
51
73
|
if (patch.maxRuns !== undefined || patch.maxDuration !== undefined) {
|
|
52
74
|
const limits = updated.limits ? { ...updated.limits } : {};
|
|
53
75
|
if (patch.maxRuns !== undefined) {
|
|
@@ -64,6 +86,85 @@ export function applyTriggerPatch(definition, input, opts = {}) {
|
|
|
64
86
|
}
|
|
65
87
|
updated.limits = Object.keys(limits).length > 0 ? limits : undefined;
|
|
66
88
|
}
|
|
89
|
+
// feedback strategy
|
|
90
|
+
if (patch.feedback !== undefined) {
|
|
91
|
+
updated.feedback.strategy = patch.feedback;
|
|
92
|
+
if (patch.feedback !== 'target')
|
|
93
|
+
delete updated.feedback.target;
|
|
94
|
+
}
|
|
95
|
+
// feedback target fields
|
|
96
|
+
const hasTargetPatch = patch.targetChannel !== undefined
|
|
97
|
+
|| patch.targetChannelId !== undefined
|
|
98
|
+
|| patch.targetChatType !== undefined
|
|
99
|
+
|| patch.targetSession !== undefined
|
|
100
|
+
|| patch.targetThreadId !== undefined;
|
|
101
|
+
if (hasTargetPatch) {
|
|
102
|
+
if ((patch.targetChannel === undefined) !== (patch.targetChannelId === undefined)) {
|
|
103
|
+
throw new Error('targetChannel 与 targetChannelId 必须同时指定');
|
|
104
|
+
}
|
|
105
|
+
const prev = updated.feedback.target;
|
|
106
|
+
const next = {
|
|
107
|
+
channelKey: patch.targetChannel ?? prev?.channelKey ?? '',
|
|
108
|
+
channelId: patch.targetChannelId ?? prev?.channelId ?? '',
|
|
109
|
+
session: patch.targetSession ?? prev?.session ?? 'main',
|
|
110
|
+
threadId: patch.targetThreadId === null || patch.targetThreadId === ''
|
|
111
|
+
? undefined
|
|
112
|
+
: (patch.targetThreadId ?? prev?.threadId),
|
|
113
|
+
chatType: patch.targetChatType === null
|
|
114
|
+
? undefined
|
|
115
|
+
: (patch.targetChatType ?? prev?.chatType),
|
|
116
|
+
};
|
|
117
|
+
if (!next.channelKey || !next.channelId) {
|
|
118
|
+
throw new Error('target 修改需要提供有效的 channelKey 和 channelId');
|
|
119
|
+
}
|
|
120
|
+
updated.feedback.target = next;
|
|
121
|
+
if (updated.feedback.strategy !== 'target')
|
|
122
|
+
updated.feedback.strategy = 'target';
|
|
123
|
+
}
|
|
124
|
+
// execution fields
|
|
125
|
+
if (patch.baseagent !== undefined) {
|
|
126
|
+
if (updated.execution.type !== 'trigger_session') {
|
|
127
|
+
throw new Error('baseagent 仅适用于 trigger_session 类型');
|
|
128
|
+
}
|
|
129
|
+
setOptional(updated.execution, 'baseagent', patch.baseagent);
|
|
130
|
+
}
|
|
131
|
+
if (patch.triggerThread !== undefined)
|
|
132
|
+
setOptional(updated.execution, 'thread', patch.triggerThread);
|
|
133
|
+
if (patch.onError !== undefined)
|
|
134
|
+
updated.execution.onError = patch.onError;
|
|
135
|
+
if (patch.noopSentinel !== undefined) {
|
|
136
|
+
if (patch.noopSentinel === null || patch.noopSentinel === '')
|
|
137
|
+
updated.execution.noopSentinel = '[[NOOP]]';
|
|
138
|
+
else
|
|
139
|
+
updated.execution.noopSentinel = patch.noopSentinel;
|
|
140
|
+
}
|
|
141
|
+
// reliability retry
|
|
142
|
+
if (patch.retryAttempts !== undefined || patch.retryBackoff !== undefined) {
|
|
143
|
+
const retry = { ...updated.reliability.retry };
|
|
144
|
+
if (patch.retryAttempts !== undefined)
|
|
145
|
+
retry.maxAttempts = patch.retryAttempts ?? 0;
|
|
146
|
+
if (patch.retryBackoff !== undefined)
|
|
147
|
+
retry.backoffMs = patch.retryBackoff ?? 30_000;
|
|
148
|
+
updated.reliability = { ...updated.reliability, retry };
|
|
149
|
+
}
|
|
150
|
+
// script fields
|
|
151
|
+
if (patch.scriptRuntime !== undefined || patch.scriptArgs !== undefined || patch.scriptTimeout !== undefined) {
|
|
152
|
+
if (updated.execution.type !== 'script' || !updated.execution.script) {
|
|
153
|
+
throw new Error('scriptRuntime/scriptArgs/scriptTimeout 仅适用于 script 类型 trigger');
|
|
154
|
+
}
|
|
155
|
+
const script = { ...updated.execution.script };
|
|
156
|
+
if (patch.scriptRuntime !== undefined)
|
|
157
|
+
script.runtime = patch.scriptRuntime;
|
|
158
|
+
if (patch.scriptArgs !== undefined) {
|
|
159
|
+
if (patch.scriptArgs === null)
|
|
160
|
+
delete script.args;
|
|
161
|
+
else
|
|
162
|
+
script.args = patch.scriptArgs;
|
|
163
|
+
}
|
|
164
|
+
if (patch.scriptTimeout !== undefined)
|
|
165
|
+
script.timeoutMs = patch.scriptTimeout ?? 30_000;
|
|
166
|
+
updated.execution.script = script;
|
|
167
|
+
}
|
|
67
168
|
return normalizeTriggerDefinition({
|
|
68
169
|
...updated,
|
|
69
170
|
id: definition.id,
|
|
@@ -98,7 +199,9 @@ export function replaceEditableTriggerDefinition(existing, input) {
|
|
|
98
199
|
}
|
|
99
200
|
function assertBaseagentImmutable(existing, updated) {
|
|
100
201
|
if (existing.execution.baseagent !== updated.execution.baseagent) {
|
|
101
|
-
|
|
202
|
+
if (existing.execution.type !== 'trigger_session' || updated.execution.type !== 'trigger_session') {
|
|
203
|
+
throw new Error('execution.baseagent is only mutable between trigger_session Triggers');
|
|
204
|
+
}
|
|
102
205
|
}
|
|
103
206
|
}
|
|
104
207
|
function applySourcePatch(updated, patch) {
|
|
@@ -120,12 +120,12 @@ export class TriggerRuntimeScheduler {
|
|
|
120
120
|
}
|
|
121
121
|
listItems(definitions) {
|
|
122
122
|
const latestRuns = this.audit.latest(definitions.map(definition => definition.id));
|
|
123
|
-
return definitions.map(definition => this.buildListItem(definition, latestRuns.get(definition.id)));
|
|
123
|
+
return definitions.map(definition => this.buildListItem(definition, latestRuns.get(definition.id), this.stats(definition.id)));
|
|
124
124
|
}
|
|
125
125
|
listItem(definition) {
|
|
126
|
-
return this.buildListItem(definition, this.audit.recent(definition.id, 1)[0]);
|
|
126
|
+
return this.buildListItem(definition, this.audit.recent(definition.id, 1)[0], this.stats(definition.id));
|
|
127
127
|
}
|
|
128
|
-
buildListItem(definition, lastRun) {
|
|
128
|
+
buildListItem(definition, lastRun, stats = this.stats(definition.id)) {
|
|
129
129
|
const schedule = this.state.readSchedule(definition.id);
|
|
130
130
|
return {
|
|
131
131
|
id: definition.id,
|
|
@@ -139,6 +139,10 @@ export class TriggerRuntimeScheduler {
|
|
|
139
139
|
...(definition.feedback.target ? { target: definition.feedback.target } : {}),
|
|
140
140
|
},
|
|
141
141
|
...(schedule ? { nextFireAt: schedule.nextFireAt } : {}),
|
|
142
|
+
fireCount: stats.fireCount,
|
|
143
|
+
failCount: stats.failCount,
|
|
144
|
+
...(stats.lastFiredAt !== undefined ? { lastFiredAt: stats.lastFiredAt } : {}),
|
|
145
|
+
...(stats.lastResult ? { lastResult: stats.lastResult } : {}),
|
|
142
146
|
...(lastRun ? { lastStatus: lastRun.status, lastFinishedAt: lastRun.finishedAt } : {}),
|
|
143
147
|
updatedAt: definition.updatedAt,
|
|
144
148
|
};
|
|
@@ -627,6 +631,7 @@ export class TriggerRuntimeScheduler {
|
|
|
627
631
|
effects: feedbackResult.effects,
|
|
628
632
|
error: feedbackResult.error,
|
|
629
633
|
conflictRunId: feedbackResult.conflictRunId,
|
|
634
|
+
interruption: feedbackResult.interruption,
|
|
630
635
|
causation,
|
|
631
636
|
});
|
|
632
637
|
if (!payload.dryRun) {
|
|
@@ -1147,6 +1152,7 @@ export class TriggerRuntimeScheduler {
|
|
|
1147
1152
|
anomalies,
|
|
1148
1153
|
effects: feedbackResult.effects,
|
|
1149
1154
|
error: feedbackResult.error,
|
|
1155
|
+
interruption: feedbackResult.interruption,
|
|
1150
1156
|
causation,
|
|
1151
1157
|
});
|
|
1152
1158
|
this.audit.write(audit);
|
|
@@ -1469,6 +1475,7 @@ export class TriggerRuntimeScheduler {
|
|
|
1469
1475
|
status: input.status,
|
|
1470
1476
|
reason: input.reason,
|
|
1471
1477
|
conflictRunId: input.conflictRunId,
|
|
1478
|
+
...(input.interruption ? { interruption: input.interruption } : {}),
|
|
1472
1479
|
...(activeRun?.executionSessionId ? { executionSessionId: activeRun.executionSessionId } : {}),
|
|
1473
1480
|
...(attempts?.length ? { attempts } : {}),
|
|
1474
1481
|
...(finalAttempt ? { finalAttempt: finalAttempt.attempt } : {}),
|
|
@@ -1783,6 +1790,7 @@ export class TriggerRuntimeScheduler {
|
|
|
1783
1790
|
type: 'trigger:skipped',
|
|
1784
1791
|
...base,
|
|
1785
1792
|
reason: audit.reason ?? 'skipped',
|
|
1793
|
+
...(audit.interruption ?? {}),
|
|
1786
1794
|
});
|
|
1787
1795
|
}
|
|
1788
1796
|
}
|
|
@@ -1900,6 +1908,15 @@ function errorReply(runId, durationMs, reason, text) {
|
|
|
1900
1908
|
}
|
|
1901
1909
|
function attemptSummaryFromExecution(attempt, attemptId, execution) {
|
|
1902
1910
|
if (execution.feedbackResult) {
|
|
1911
|
+
if (execution.feedbackResult.interruption) {
|
|
1912
|
+
return {
|
|
1913
|
+
status: 'interrupted',
|
|
1914
|
+
error: {
|
|
1915
|
+
code: execution.feedbackResult.interruption.reasonCode,
|
|
1916
|
+
message: execution.feedbackResult.reason ?? execution.feedbackResult.interruption.reasonCode,
|
|
1917
|
+
},
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1903
1920
|
const failed = execution.feedbackResult.status !== 'completed';
|
|
1904
1921
|
return {
|
|
1905
1922
|
status: failed ? 'failed' : 'completed',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
|
-
import { resolvePaths } from '../paths.js';
|
|
4
|
+
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
5
5
|
import { WEB_PACKAGE_NAME } from '../product.js';
|
|
6
6
|
import { resolveGlobalPkg } from './npm-ops.js';
|
|
7
7
|
import { ecwebVersionRequirementError, resolveEcwebLaunchCommand } from './ecweb-utils.js';
|
|
@@ -218,7 +218,16 @@ export function createEcwebSupervisor(options) {
|
|
|
218
218
|
detached: true,
|
|
219
219
|
stdio: ['ignore', childLogFd, childLogFd],
|
|
220
220
|
windowsHide: platform.isWindows,
|
|
221
|
-
|
|
221
|
+
// ECWeb dynamically imports EvolCore's dist modules for config,
|
|
222
|
+
// roles, authorization and session compatibility. Pin that lookup
|
|
223
|
+
// to the same package root as this daemon; otherwise a separately
|
|
224
|
+
// installed ECWeb can resolve its peer dependency and silently load
|
|
225
|
+
// a different (possibly stale) EvolCore copy from node_modules.
|
|
226
|
+
env: {
|
|
227
|
+
...process.env,
|
|
228
|
+
EVOLCORE_HOME: root,
|
|
229
|
+
EVOLCORE_PACKAGE_ROOT: getPackageRoot(),
|
|
230
|
+
},
|
|
222
231
|
});
|
|
223
232
|
}
|
|
224
233
|
catch (error) {
|
|
@@ -4,6 +4,7 @@ import { compareVersions, resolveGlobalPkg } from './npm-ops.js';
|
|
|
4
4
|
import { isWindows as platformIsWindows, resolveCommandPath } from './cross-platform.js';
|
|
5
5
|
import { MIN_ECWEB_VERSION, WEB_CLI_BIN, WEB_PACKAGE_NAME } from '../product.js';
|
|
6
6
|
import { resolvePaths } from '../paths.js';
|
|
7
|
+
import { ipcQuery } from '../ipc.js';
|
|
7
8
|
/** EC Web owns the session-log reader, so older builds cannot safely serve a newer daemon. */
|
|
8
9
|
export function isEcwebVersionSupported(version) {
|
|
9
10
|
const normalized = version?.trim();
|
|
@@ -65,34 +66,15 @@ export function resolveEcwebLaunchCommand(ecwebArgs, opts = {}) {
|
|
|
65
66
|
}
|
|
66
67
|
return { command: commandPath, args: ecwebArgs, source: 'command' };
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
* ECWeb 配对码取码 helper(共享给 daemon 和 CLI)。
|
|
70
|
-
*
|
|
71
|
-
* 配对码是 ecweb 进程自己生成并持有的内部状态。daemon/CLI 通过 ecweb 的
|
|
72
|
-
* localhost-only HTTP 接口 GET /api/pair-code 取当前码(远程访问被 ecweb 403 拒绝)。
|
|
73
|
-
*/
|
|
74
|
-
/** 经 localhost 拉取 ecweb 当前配对码(仅本机可取)。失败返回 null。
|
|
75
|
-
* authorizedBy 仅由 daemon 的 AUN 控制通道传入,用于把配对码换出的 token
|
|
76
|
-
* 绑定到实际批准的 daemon-owner;普通 CLI 取码不携带该字段。 */
|
|
77
|
-
export async function fetchEcwebPairCode(port, authorizedBy) {
|
|
69
|
+
export async function fetchRemotePairingCode(authorizedBy) {
|
|
78
70
|
try {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const controlToken = fs.readFileSync(resolvePaths().controlToken, 'utf8').trim();
|
|
85
|
-
if (controlToken)
|
|
86
|
-
headers['x-evolcore-control-token'] = controlToken;
|
|
87
|
-
}
|
|
88
|
-
catch { }
|
|
89
|
-
const resp = await fetch(`http://127.0.0.1:${port}/api/pair-code`, {
|
|
90
|
-
headers,
|
|
91
|
-
signal: AbortSignal.timeout(2000),
|
|
92
|
-
});
|
|
93
|
-
if (!resp.ok)
|
|
71
|
+
const response = await ipcQuery(resolvePaths().socket, {
|
|
72
|
+
type: 'remote-auth.pair-code',
|
|
73
|
+
...(authorizedBy?.trim() ? { authorizedBy: authorizedBy.trim() } : {}),
|
|
74
|
+
}, 2_000);
|
|
75
|
+
if (!response?.ok || typeof response.code !== 'string' || typeof response.expiresAt !== 'number')
|
|
94
76
|
return null;
|
|
95
|
-
return
|
|
77
|
+
return { code: response.code, expiresAt: response.expiresAt };
|
|
96
78
|
}
|
|
97
79
|
catch {
|
|
98
80
|
return null;
|
|
@@ -380,6 +380,13 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
380
380
|
}
|
|
381
381
|
// 回退到原有的错误消息匹配逻辑
|
|
382
382
|
const msg = error?.message || String(error);
|
|
383
|
+
// Codex app-server reports this when another app-server still owns the
|
|
384
|
+
// thread writer. Surface the handoff condition instead of hiding it behind
|
|
385
|
+
// the generic "processing failed" message.
|
|
386
|
+
if (error?.code === 'THREAD_ACTIVE_WRITER' || /already has an active writer/i.test(msg)) {
|
|
387
|
+
const prefix = includeEmoji ? '⚠️ ' : '';
|
|
388
|
+
return `${prefix}Codex 会话正在由另一个执行通道占用,请稍后重试(THREAD_ACTIVE_WRITER)`;
|
|
389
|
+
}
|
|
383
390
|
// 字典优先 — 命中且有自定义消息则直接返回
|
|
384
391
|
const rule = matchErrorRule(msg.toLowerCase());
|
|
385
392
|
if (rule?.message)
|
package/dist/utils/logger.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { resolvePaths } from '../paths.js';
|
|
3
3
|
import { LogWriter } from './log-writer.js';
|
|
4
|
-
import { classifyToolErrorCode } from '../core/permission/tool-error-code.js';
|
|
4
|
+
import { classifyToolErrorCode, normalizeToolErrorCode } from '../core/permission/tool-error-code.js';
|
|
5
5
|
import { normalizePermissionMode } from '../core/permission/mode.js';
|
|
6
6
|
import { buildToolLifecycleEventKey } from '../core/audit/event-key.js';
|
|
7
7
|
let currentLevel = process.env.LOG_LEVEL || 'INFO';
|
|
@@ -95,8 +95,8 @@ export function normalizeStructuredLog(data) {
|
|
|
95
95
|
const isToolUse = eventType === 'tool:use' || eventType === 'tool_use';
|
|
96
96
|
const isError = data.isError ?? data.is_error ?? (data.ok === false ? true : undefined)
|
|
97
97
|
?? nested?.isError ?? nested?.is_error ?? (nested?.ok === false ? true : undefined);
|
|
98
|
-
const
|
|
99
|
-
|
|
98
|
+
const decisionSource = data.decisionSource ?? data.decision_source
|
|
99
|
+
?? nested?.decisionSource ?? nested?.decision_source;
|
|
100
100
|
const decision = data.decision
|
|
101
101
|
?? data.status
|
|
102
102
|
?? (isToolResult && isError === true ? 'error' : undefined)
|
|
@@ -113,8 +113,11 @@ export function normalizeStructuredLog(data) {
|
|
|
113
113
|
?? nested?.errorCode
|
|
114
114
|
?? nested?.error_code;
|
|
115
115
|
const errorCode = isToolResult && isError === true
|
|
116
|
-
? classifyToolErrorCode({ errorCode: explicitErrorCode,
|
|
116
|
+
? classifyToolErrorCode({ errorCode: explicitErrorCode, decisionSource })
|
|
117
117
|
: explicitErrorCode;
|
|
118
|
+
const classificationMissing = isToolResult && isError === true
|
|
119
|
+
&& !normalizeToolErrorCode(explicitErrorCode)
|
|
120
|
+
&& decisionSource !== 'policy' && decisionSource !== 'approval';
|
|
118
121
|
const lifecycleRecord = isToolUse || isToolResult;
|
|
119
122
|
const lifecycleEventKey = lifecycleRecord
|
|
120
123
|
? (eventKey ?? buildToolLifecycleEventKey({
|
|
@@ -137,10 +140,12 @@ export function normalizeStructuredLog(data) {
|
|
|
137
140
|
...(agentName ? { agentName } : lifecycleRecord ? { agentName: 'unknown' } : {}),
|
|
138
141
|
...(permissionMode ? { permissionMode } : lifecycleRecord ? { permissionMode: 'unknown' } : {}),
|
|
139
142
|
...(toolName ? { toolName } : {}),
|
|
143
|
+
...(decisionSource ? { decisionSource } : {}),
|
|
140
144
|
...(decision ? { decision } : {}),
|
|
141
145
|
...(executed !== undefined ? { executed } : {}),
|
|
142
146
|
...(executionState ? { executionState } : {}),
|
|
143
147
|
...(errorCode ? { errorCode } : {}),
|
|
148
|
+
...(classificationMissing ? { classificationMissing: true } : {}),
|
|
144
149
|
...(missingContextFields.length > 0 ? { contextMissing: missingContextFields } : {}),
|
|
145
150
|
};
|
|
146
151
|
}
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -14,7 +14,7 @@ import { getPackageRoot } from '../paths.js';
|
|
|
14
14
|
import { isWindows } from './cross-platform.js';
|
|
15
15
|
const execFileAsync = promisify(execFile);
|
|
16
16
|
// ── npm install -g (shared) ────────────────────────────────────────────────
|
|
17
|
-
export async function npmInstallGlobal(pkg) {
|
|
17
|
+
export async function npmInstallGlobal(pkg, options = {}) {
|
|
18
18
|
let cmd;
|
|
19
19
|
let args;
|
|
20
20
|
// Windows: run via cmd /c to avoid shell:true deprecation warning on Node 22.
|
|
@@ -28,14 +28,24 @@ export async function npmInstallGlobal(pkg) {
|
|
|
28
28
|
args = ['install', '-g', pkg];
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
await execFileAsync(cmd, args, {
|
|
31
|
+
await execFileAsync(cmd, args, {
|
|
32
|
+
timeout: options.timeoutMs ?? 180000,
|
|
33
|
+
windowsHide: isWindows,
|
|
34
|
+
signal: options.signal,
|
|
35
|
+
});
|
|
32
36
|
}
|
|
33
37
|
catch (e) {
|
|
38
|
+
if (options.signal?.aborted)
|
|
39
|
+
throw e;
|
|
34
40
|
if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
|
|
35
41
|
if (isWindows) {
|
|
36
42
|
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
37
43
|
}
|
|
38
|
-
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], {
|
|
44
|
+
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], {
|
|
45
|
+
timeout: options.timeoutMs ?? 180000,
|
|
46
|
+
windowsHide: false,
|
|
47
|
+
signal: options.signal,
|
|
48
|
+
});
|
|
39
49
|
}
|
|
40
50
|
else {
|
|
41
51
|
throw e;
|
|
@@ -119,15 +129,19 @@ export function resolveGlobalPkg(pkgName) {
|
|
|
119
129
|
* 使用 HTTP fetch 直接查 registry API,不依赖 npm CLI。
|
|
120
130
|
* 超时 10 秒,失败返回 null。
|
|
121
131
|
*/
|
|
122
|
-
export async function checkLatestVersion(pkg = 'evolcore') {
|
|
132
|
+
export async function checkLatestVersion(pkg = 'evolcore', timeoutMs = 10000, signal) {
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
const abort = () => controller.abort();
|
|
135
|
+
if (signal?.aborted)
|
|
136
|
+
abort();
|
|
137
|
+
else
|
|
138
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
139
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
123
140
|
try {
|
|
124
|
-
const controller = new AbortController();
|
|
125
|
-
const timer = setTimeout(() => controller.abort(), 10000);
|
|
126
141
|
const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
127
142
|
signal: controller.signal,
|
|
128
143
|
headers: { 'Accept': 'application/json' },
|
|
129
144
|
});
|
|
130
|
-
clearTimeout(timer);
|
|
131
145
|
if (!res.ok)
|
|
132
146
|
return null;
|
|
133
147
|
const data = await res.json();
|
|
@@ -136,20 +150,26 @@ export async function checkLatestVersion(pkg = 'evolcore') {
|
|
|
136
150
|
catch {
|
|
137
151
|
return null;
|
|
138
152
|
}
|
|
153
|
+
finally {
|
|
154
|
+
clearTimeout(timer);
|
|
155
|
+
signal?.removeEventListener('abort', abort);
|
|
156
|
+
}
|
|
139
157
|
}
|
|
140
158
|
/**
|
|
141
159
|
* 完整升级流程:检查 → 比较 → 安装(失败重试一次)
|
|
142
160
|
*/
|
|
143
|
-
export async function tryUpgrade() {
|
|
161
|
+
export async function tryUpgrade(options = {}) {
|
|
144
162
|
// 开发模式跳过
|
|
145
163
|
if (isLinkedInstall()) {
|
|
146
164
|
return { status: 'skipped' };
|
|
147
165
|
}
|
|
148
166
|
const localVer = getLocalVersion();
|
|
149
167
|
// 查询 registry
|
|
150
|
-
const remoteVer = await checkLatestVersion();
|
|
168
|
+
const remoteVer = await checkLatestVersion('evolcore', options.checkTimeoutMs, options.signal);
|
|
151
169
|
if (!remoteVer) {
|
|
152
|
-
return
|
|
170
|
+
return options.signal?.aborted
|
|
171
|
+
? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
|
|
172
|
+
: { status: 'skipped', error: 'Failed to check remote version' };
|
|
153
173
|
}
|
|
154
174
|
// 版本比较
|
|
155
175
|
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
@@ -159,11 +179,16 @@ export async function tryUpgrade() {
|
|
|
159
179
|
let lastError;
|
|
160
180
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
161
181
|
try {
|
|
162
|
-
await npmInstallGlobal('evolcore@latest'
|
|
182
|
+
await npmInstallGlobal('evolcore@latest', {
|
|
183
|
+
timeoutMs: options.installTimeoutMs,
|
|
184
|
+
signal: options.signal,
|
|
185
|
+
});
|
|
163
186
|
return { status: 'upgraded', from: localVer, to: remoteVer };
|
|
164
187
|
}
|
|
165
188
|
catch (e) {
|
|
166
189
|
lastError = e.stderr || e.message || String(e);
|
|
190
|
+
if (options.signal?.aborted)
|
|
191
|
+
break;
|
|
167
192
|
}
|
|
168
193
|
}
|
|
169
194
|
return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
|
|
@@ -173,7 +198,7 @@ export async function tryUpgrade() {
|
|
|
173
198
|
* 仅在包已安装时检查升级,未安装则跳过(resolveFn 返回 null)。
|
|
174
199
|
* 用于 EC Web 等真正独立于 EvolCore 的全局包。
|
|
175
200
|
*/
|
|
176
|
-
export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
|
|
201
|
+
export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName, options = {}) {
|
|
177
202
|
if (isLinkedInstall()) {
|
|
178
203
|
return { status: 'skipped' };
|
|
179
204
|
}
|
|
@@ -182,9 +207,11 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
|
|
|
182
207
|
return { status: 'skipped' }; // not installed, skip
|
|
183
208
|
}
|
|
184
209
|
const localVer = installed.version;
|
|
185
|
-
const remoteVer = await checkLatestVersion(pkgName);
|
|
210
|
+
const remoteVer = await checkLatestVersion(pkgName, options.checkTimeoutMs, options.signal);
|
|
186
211
|
if (!remoteVer) {
|
|
187
|
-
return
|
|
212
|
+
return options.signal?.aborted
|
|
213
|
+
? { status: 'failed', from: localVer, error: 'Upgrade cancelled or timed out' }
|
|
214
|
+
: { status: 'skipped', error: 'Failed to check remote version' };
|
|
188
215
|
}
|
|
189
216
|
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
190
217
|
return { status: 'no-update', from: localVer };
|
|
@@ -192,11 +219,16 @@ export async function tryUpgradeGlobalPkg(resolveInstalled, pkgName) {
|
|
|
192
219
|
let lastError;
|
|
193
220
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
194
221
|
try {
|
|
195
|
-
await npmInstallGlobal(`${pkgName}@latest
|
|
222
|
+
await npmInstallGlobal(`${pkgName}@latest`, {
|
|
223
|
+
timeoutMs: options.installTimeoutMs,
|
|
224
|
+
signal: options.signal,
|
|
225
|
+
});
|
|
196
226
|
return { status: 'upgraded', from: localVer, to: remoteVer };
|
|
197
227
|
}
|
|
198
228
|
catch (e) {
|
|
199
229
|
lastError = e.stderr || e.message || String(e);
|
|
230
|
+
if (options.signal?.aborted)
|
|
231
|
+
break;
|
|
200
232
|
}
|
|
201
233
|
}
|
|
202
234
|
return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
|