evolcore 0.0.4 → 0.0.6
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 +65 -3
- package/README.md +4 -12
- package/dist/agents/baseagent.js +39 -0
- package/dist/agents/claude-runner.js +10 -2
- package/dist/agents/codex-runner.js +37 -8
- package/dist/agents/ecagent-runner.js +1159 -0
- package/dist/agents/gemini-runner.js +4 -1
- package/dist/agents/runner-types.js +20 -4
- package/dist/aun/msg/history.js +32 -9
- package/dist/aun/outbox.js +4 -9
- package/dist/channels/aun.js +32 -6
- package/dist/channels/daemon.js +7 -4
- package/dist/channels/dingtalk.js +3 -5
- package/dist/channels/feishu.js +16 -108
- package/dist/channels/wechat.js +4 -4
- package/dist/channels/wecom.js +4 -6
- package/dist/cli/agent-command.js +1 -1
- package/dist/cli/agent.js +42 -11
- package/dist/cli/aun-commands.js +50 -3
- package/dist/cli/daemon-commands.js +127 -21
- package/dist/cli/data-command.js +132 -0
- package/dist/cli/index.js +11 -3
- package/dist/cli/init.js +116 -37
- package/dist/cli/net-check.js +9 -20
- package/dist/cli/restart-monitor.js +11 -2
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg.js +12 -25
- package/dist/config/access-policy-domain.js +0 -20
- package/dist/config/access-policy.js +5 -29
- package/dist/config/builtin-roles.js +3 -1
- package/dist/config/config-field-policy.js +1 -1
- package/dist/config/gateway-config.js +21 -4
- package/dist/config/resolved-config-op.js +10 -0
- package/dist/core/auth/operation-catalog.js +1 -1
- package/dist/core/command/command-handler.js +2 -2
- package/dist/core/command/connect-menu.js +20 -42
- package/dist/core/command/menu-handler.js +11 -9
- package/dist/core/command/role-menu.js +27 -0
- package/dist/core/command/slash-handler.js +18 -14
- package/dist/core/data-migration.js +1434 -0
- package/dist/core/handoff/store.js +14 -4
- package/dist/core/inference/text-inference.js +42 -1
- package/dist/core/message/inbound-admission.js +23 -13
- package/dist/core/message/pending-hints.js +1 -1
- package/dist/core/message/response-engine.js +406 -167
- package/dist/core/message/retry-scheduler.js +71 -0
- package/dist/core/model/model-catalog.js +7 -2
- package/dist/core/model/model-fallback.js +81 -0
- package/dist/core/protected-paths.js +41 -8
- package/dist/core/session/adapters/ecagent-session-file-adapter.js +112 -0
- package/dist/core/session/session-fs-store.js +300 -61
- package/dist/core/session/session-manager.js +225 -21
- package/dist/core/session/session-title.js +1 -0
- package/dist/core/session/session-turn-coordinator.js +8 -1
- package/dist/core/system-channels.js +1 -0
- package/dist/index.js +10 -9
- package/dist/paths.js +35 -27
- package/dist/trigger/feedback.js +16 -0
- package/dist/trigger/legacy-session-history.js +19 -0
- package/dist/trigger/manager.js +12 -1
- package/dist/trigger/parser.js +4 -2
- package/dist/trigger/scheduler.js +185 -15
- package/dist/trigger/session-lock.js +62 -0
- package/dist/trigger/state.js +64 -0
- package/dist/trigger/validation.js +32 -2
- package/dist/utils/aid-bind.js +1 -1
- package/dist/utils/cross-platform.js +8 -2
- package/dist/utils/error-utils.js +9 -0
- package/dist/utils/process-introspect.js +19 -3
- package/dist/utils/welcome.js +2 -3
- package/ecagent/LICENSE +21 -0
- package/ecagent/NOTICE +11 -0
- package/ecagent/README.md +61 -0
- package/ecagent/dist/agent-loop.d.ts +24 -0
- package/ecagent/dist/agent-loop.d.ts.map +1 -0
- package/ecagent/dist/agent-loop.js +547 -0
- package/ecagent/dist/agent-loop.js.map +1 -0
- package/ecagent/dist/agent.d.ts +127 -0
- package/ecagent/dist/agent.d.ts.map +1 -0
- package/ecagent/dist/agent.js +386 -0
- package/ecagent/dist/agent.js.map +1 -0
- package/ecagent/dist/harness/agent-harness.d.ts +95 -0
- package/ecagent/dist/harness/agent-harness.d.ts.map +1 -0
- package/ecagent/dist/harness/agent-harness.js +926 -0
- package/ecagent/dist/harness/agent-harness.js.map +1 -0
- package/ecagent/dist/harness/compaction/branch-summarization.d.ts +51 -0
- package/ecagent/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js +174 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/ecagent/dist/harness/compaction/compaction.d.ts +95 -0
- package/ecagent/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/compaction.js +528 -0
- package/ecagent/dist/harness/compaction/compaction.js.map +1 -0
- package/ecagent/dist/harness/compaction/utils.d.ts +25 -0
- package/ecagent/dist/harness/compaction/utils.d.ts.map +1 -0
- package/ecagent/dist/harness/compaction/utils.js +131 -0
- package/ecagent/dist/harness/compaction/utils.js.map +1 -0
- package/ecagent/dist/harness/env/nodejs.d.ts +51 -0
- package/ecagent/dist/harness/env/nodejs.d.ts.map +1 -0
- package/ecagent/dist/harness/env/nodejs.js +513 -0
- package/ecagent/dist/harness/env/nodejs.js.map +1 -0
- package/ecagent/dist/harness/messages.d.ts +51 -0
- package/ecagent/dist/harness/messages.d.ts.map +1 -0
- package/ecagent/dist/harness/messages.js +102 -0
- package/ecagent/dist/harness/messages.js.map +1 -0
- package/ecagent/dist/harness/prompt-templates.d.ts +48 -0
- package/ecagent/dist/harness/prompt-templates.d.ts.map +1 -0
- package/ecagent/dist/harness/prompt-templates.js +230 -0
- package/ecagent/dist/harness/prompt-templates.js.map +1 -0
- package/ecagent/dist/harness/session/jsonl-repo.d.ts +26 -0
- package/ecagent/dist/harness/session/jsonl-repo.d.ts.map +1 -0
- package/ecagent/dist/harness/session/jsonl-repo.js +100 -0
- package/ecagent/dist/harness/session/jsonl-repo.js.map +1 -0
- package/ecagent/dist/harness/session/jsonl-storage.d.ts +34 -0
- package/ecagent/dist/harness/session/jsonl-storage.d.ts.map +1 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +234 -0
- package/ecagent/dist/harness/session/jsonl-storage.js.map +1 -0
- package/ecagent/dist/harness/session/memory-repo.d.ts +18 -0
- package/ecagent/dist/harness/session/memory-repo.d.ts.map +1 -0
- package/ecagent/dist/harness/session/memory-repo.js +44 -0
- package/ecagent/dist/harness/session/memory-repo.js.map +1 -0
- package/ecagent/dist/harness/session/memory-storage.d.ts +25 -0
- package/ecagent/dist/harness/session/memory-storage.d.ts.map +1 -0
- package/ecagent/dist/harness/session/memory-storage.js +111 -0
- package/ecagent/dist/harness/session/memory-storage.js.map +1 -0
- package/ecagent/dist/harness/session/repo-utils.d.ts +11 -0
- package/ecagent/dist/harness/session/repo-utils.d.ts.map +1 -0
- package/ecagent/dist/harness/session/repo-utils.js +39 -0
- package/ecagent/dist/harness/session/repo-utils.js.map +1 -0
- package/ecagent/dist/harness/session/session.d.ts +47 -0
- package/ecagent/dist/harness/session/session.d.ts.map +1 -0
- package/ecagent/dist/harness/session/session.js +245 -0
- package/ecagent/dist/harness/session/session.js.map +1 -0
- package/ecagent/dist/harness/session/uuid.d.ts +2 -0
- package/ecagent/dist/harness/session/uuid.d.ts.map +1 -0
- package/ecagent/dist/harness/session/uuid.js +50 -0
- package/ecagent/dist/harness/session/uuid.js.map +1 -0
- package/ecagent/dist/harness/skills.d.ts +44 -0
- package/ecagent/dist/harness/skills.d.ts.map +1 -0
- package/ecagent/dist/harness/skills.js +311 -0
- package/ecagent/dist/harness/skills.js.map +1 -0
- package/ecagent/dist/harness/system-prompt.d.ts +3 -0
- package/ecagent/dist/harness/system-prompt.d.ts.map +1 -0
- package/ecagent/dist/harness/system-prompt.js +30 -0
- package/ecagent/dist/harness/system-prompt.js.map +1 -0
- package/ecagent/dist/harness/types.d.ts +619 -0
- package/ecagent/dist/harness/types.d.ts.map +1 -0
- package/ecagent/dist/harness/types.js +81 -0
- package/ecagent/dist/harness/types.js.map +1 -0
- package/ecagent/dist/harness/utils/shell-output.d.ts +14 -0
- package/ecagent/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/ecagent/dist/harness/utils/shell-output.js +126 -0
- package/ecagent/dist/harness/utils/shell-output.js.map +1 -0
- package/ecagent/dist/harness/utils/truncate.d.ts +70 -0
- package/ecagent/dist/harness/utils/truncate.d.ts.map +1 -0
- package/ecagent/dist/harness/utils/truncate.js +290 -0
- package/ecagent/dist/harness/utils/truncate.js.map +1 -0
- package/ecagent/dist/index.d.ts +22 -0
- package/ecagent/dist/index.d.ts.map +1 -0
- package/ecagent/dist/index.js +27 -0
- package/ecagent/dist/index.js.map +1 -0
- package/ecagent/dist/node.d.ts +3 -0
- package/ecagent/dist/node.d.ts.map +1 -0
- package/ecagent/dist/node.js +3 -0
- package/ecagent/dist/node.js.map +1 -0
- package/ecagent/dist/proxy.d.ts +69 -0
- package/ecagent/dist/proxy.d.ts.map +1 -0
- package/ecagent/dist/proxy.js +278 -0
- package/ecagent/dist/proxy.js.map +1 -0
- package/ecagent/dist/runtime/api/lazy.d.ts +15 -0
- package/ecagent/dist/runtime/api/lazy.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/lazy.js +59 -0
- package/ecagent/dist/runtime/api/lazy.js.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.d.ts +97 -0
- package/ecagent/dist/runtime/api/pi-messages.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.js +307 -0
- package/ecagent/dist/runtime/api/pi-messages.js.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts +3 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.d.ts.map +1 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.js +3 -0
- package/ecagent/dist/runtime/api/pi-messages.lazy.js.map +1 -0
- package/ecagent/dist/runtime/auth/context.d.ts +7 -0
- package/ecagent/dist/runtime/auth/context.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/context.js +42 -0
- package/ecagent/dist/runtime/auth/context.js.map +1 -0
- package/ecagent/dist/runtime/auth/credential-store.d.ts +16 -0
- package/ecagent/dist/runtime/auth/credential-store.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/credential-store.js +39 -0
- package/ecagent/dist/runtime/auth/credential-store.js.map +1 -0
- package/ecagent/dist/runtime/auth/helpers.d.ts +20 -0
- package/ecagent/dist/runtime/auth/helpers.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/helpers.js +46 -0
- package/ecagent/dist/runtime/auth/helpers.js.map +1 -0
- package/ecagent/dist/runtime/auth/resolve.d.ts +26 -0
- package/ecagent/dist/runtime/auth/resolve.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/resolve.js +100 -0
- package/ecagent/dist/runtime/auth/resolve.js.map +1 -0
- package/ecagent/dist/runtime/auth/types.d.ts +180 -0
- package/ecagent/dist/runtime/auth/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/auth/types.js +2 -0
- package/ecagent/dist/runtime/auth/types.js.map +1 -0
- package/ecagent/dist/runtime/compat.d.ts +28 -0
- package/ecagent/dist/runtime/compat.d.ts.map +1 -0
- package/ecagent/dist/runtime/compat.js +86 -0
- package/ecagent/dist/runtime/compat.js.map +1 -0
- package/ecagent/dist/runtime/index.d.ts +20 -0
- package/ecagent/dist/runtime/index.d.ts.map +1 -0
- package/ecagent/dist/runtime/index.js +17 -0
- package/ecagent/dist/runtime/index.js.map +1 -0
- package/ecagent/dist/runtime/models.d.ts +142 -0
- package/ecagent/dist/runtime/models.d.ts.map +1 -0
- package/ecagent/dist/runtime/models.js +245 -0
- package/ecagent/dist/runtime/models.js.map +1 -0
- package/ecagent/dist/runtime/oauth.d.ts +2 -0
- package/ecagent/dist/runtime/oauth.d.ts.map +1 -0
- package/ecagent/dist/runtime/oauth.js +2 -0
- package/ecagent/dist/runtime/oauth.js.map +1 -0
- package/ecagent/dist/runtime/providers/faux.d.ts +97 -0
- package/ecagent/dist/runtime/providers/faux.d.ts.map +1 -0
- package/ecagent/dist/runtime/providers/faux.js +395 -0
- package/ecagent/dist/runtime/providers/faux.js.map +1 -0
- package/ecagent/dist/runtime/types.d.ts +607 -0
- package/ecagent/dist/runtime/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/types.js +2 -0
- package/ecagent/dist/runtime/types.js.map +1 -0
- package/ecagent/dist/runtime/utils/diagnostics.d.ts +19 -0
- package/ecagent/dist/runtime/utils/diagnostics.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/diagnostics.js +25 -0
- package/ecagent/dist/runtime/utils/diagnostics.js.map +1 -0
- package/ecagent/dist/runtime/utils/event-stream.d.ts +21 -0
- package/ecagent/dist/runtime/utils/event-stream.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/event-stream.js +77 -0
- package/ecagent/dist/runtime/utils/event-stream.js.map +1 -0
- package/ecagent/dist/runtime/utils/headers.d.ts +4 -0
- package/ecagent/dist/runtime/utils/headers.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/headers.js +18 -0
- package/ecagent/dist/runtime/utils/headers.js.map +1 -0
- package/ecagent/dist/runtime/utils/json-parse.d.ts +16 -0
- package/ecagent/dist/runtime/utils/json-parse.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/json-parse.js +113 -0
- package/ecagent/dist/runtime/utils/json-parse.js.map +1 -0
- package/ecagent/dist/runtime/utils/oauth/types.d.ts +64 -0
- package/ecagent/dist/runtime/utils/oauth/types.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/oauth/types.js +2 -0
- package/ecagent/dist/runtime/utils/oauth/types.js.map +1 -0
- package/ecagent/dist/runtime/utils/overflow.d.ts +58 -0
- package/ecagent/dist/runtime/utils/overflow.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/overflow.js +158 -0
- package/ecagent/dist/runtime/utils/overflow.js.map +1 -0
- package/ecagent/dist/runtime/utils/provider-env.d.ts +7 -0
- package/ecagent/dist/runtime/utils/provider-env.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/provider-env.js +44 -0
- package/ecagent/dist/runtime/utils/provider-env.js.map +1 -0
- package/ecagent/dist/runtime/utils/retry.d.ts +12 -0
- package/ecagent/dist/runtime/utils/retry.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/retry.js +90 -0
- package/ecagent/dist/runtime/utils/retry.js.map +1 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.d.ts +17 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.js +21 -0
- package/ecagent/dist/runtime/utils/typebox-helpers.js.map +1 -0
- package/ecagent/dist/runtime/utils/validation.d.ts +18 -0
- package/ecagent/dist/runtime/utils/validation.d.ts.map +1 -0
- package/ecagent/dist/runtime/utils/validation.js +269 -0
- package/ecagent/dist/runtime/utils/validation.js.map +1 -0
- package/ecagent/dist/types.d.ts +401 -0
- package/ecagent/dist/types.d.ts.map +1 -0
- package/ecagent/dist/types.js +2 -0
- package/ecagent/dist/types.js.map +1 -0
- package/ecagent/package.json +93 -0
- package/ecagent/src/agent-loop.ts +792 -0
- package/ecagent/src/agent.ts +575 -0
- package/ecagent/src/harness/agent-harness.ts +1029 -0
- package/ecagent/src/harness/compaction/branch-summarization.ts +261 -0
- package/ecagent/src/harness/compaction/compaction.ts +753 -0
- package/ecagent/src/harness/compaction/utils.ts +144 -0
- package/ecagent/src/harness/env/nodejs.ts +569 -0
- package/ecagent/src/harness/messages.ts +164 -0
- package/ecagent/src/harness/prompt-templates.ts +267 -0
- package/ecagent/src/harness/session/jsonl-repo.ts +179 -0
- package/ecagent/src/harness/session/jsonl-storage.ts +314 -0
- package/ecagent/src/harness/session/memory-repo.ts +50 -0
- package/ecagent/src/harness/session/memory-storage.ts +133 -0
- package/ecagent/src/harness/session/repo-utils.ts +51 -0
- package/ecagent/src/harness/session/session.ts +338 -0
- package/ecagent/src/harness/session/uuid.ts +54 -0
- package/ecagent/src/harness/skills.ts +375 -0
- package/ecagent/src/harness/system-prompt.ts +34 -0
- package/ecagent/src/harness/types.ts +838 -0
- package/ecagent/src/harness/utils/shell-output.ts +135 -0
- package/ecagent/src/harness/utils/truncate.ts +344 -0
- package/ecagent/src/index.ts +46 -0
- package/ecagent/src/node.ts +2 -0
- package/ecagent/src/proxy.ts +367 -0
- package/ecagent/src/runtime/api/lazy.ts +70 -0
- package/ecagent/src/runtime/api/pi-messages.lazy.ts +4 -0
- package/ecagent/src/runtime/api/pi-messages.ts +436 -0
- package/ecagent/src/runtime/auth/context.ts +45 -0
- package/ecagent/src/runtime/auth/credential-store.ts +47 -0
- package/ecagent/src/runtime/auth/helpers.ts +46 -0
- package/ecagent/src/runtime/auth/resolve.ts +141 -0
- package/ecagent/src/runtime/auth/types.ts +182 -0
- package/ecagent/src/runtime/compat.ts +158 -0
- package/ecagent/src/runtime/index.ts +31 -0
- package/ecagent/src/runtime/models.ts +452 -0
- package/ecagent/src/runtime/oauth.ts +1 -0
- package/ecagent/src/runtime/providers/faux.ts +538 -0
- package/ecagent/src/runtime/types.ts +718 -0
- package/ecagent/src/runtime/utils/diagnostics.ts +45 -0
- package/ecagent/src/runtime/utils/event-stream.ts +88 -0
- package/ecagent/src/runtime/utils/headers.ts +18 -0
- package/ecagent/src/runtime/utils/json-parse.ts +124 -0
- package/ecagent/src/runtime/utils/oauth/types.ts +79 -0
- package/ecagent/src/runtime/utils/overflow.ts +165 -0
- package/ecagent/src/runtime/utils/provider-env.ts +52 -0
- package/ecagent/src/runtime/utils/retry.ts +101 -0
- package/ecagent/src/runtime/utils/typebox-helpers.ts +24 -0
- package/ecagent/src/runtime/utils/validation.ts +310 -0
- package/ecagent/src/types.ts +430 -0
- package/ecagent/tsconfig.build.json +32 -0
- package/kits/docs/evolcore/config.md +4 -4
- package/kits/docs/evolcore/contact.md +2 -1
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.10.json +370 -0
- package/kits/schemas/agent-config.schema.9.json +2 -12
- package/kits/schemas/defaults.schema.4.json +93 -0
- package/kits/schemas/relation-config.schema.8.json +69 -0
- package/kits/templates/roles/admin.json +1 -1
- package/package.json +22 -6
- package/skills/eclink/SKILL.md +70 -0
- package/skills/eclink/agents/openai.yaml +4 -0
- package/assets/.env.template +0 -4
|
@@ -3,7 +3,8 @@ import fs from 'fs';
|
|
|
3
3
|
import { CronExpressionParser } from 'cron-parser';
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
import { matchTriggerEvent, TriggerEventSource } from './event-source.js';
|
|
6
|
-
import { definitionRevision, parseDurationMs, previewText, renderTemplate, resolveScriptPath, sha256, } from './validation.js';
|
|
6
|
+
import { definitionRevision, parseDurationMs, previewText, renderTemplate, resolveScriptPath, sha256, validateConcurrencyCombination, } from './validation.js';
|
|
7
|
+
import { TargetSessionLockRegistry } from './session-lock.js';
|
|
7
8
|
import { enterTrigger, formatTriggerPath } from '../core/causation/context.js';
|
|
8
9
|
import { createRootCausation } from '../core/causation/context.js';
|
|
9
10
|
import { recordCausationSpan } from '../core/causation/audit.js';
|
|
@@ -21,6 +22,7 @@ export class TriggerRuntimeScheduler {
|
|
|
21
22
|
daemonChannel;
|
|
22
23
|
runtime;
|
|
23
24
|
eventBus;
|
|
25
|
+
targetSessionLocks;
|
|
24
26
|
timers = new Map();
|
|
25
27
|
running = new Map();
|
|
26
28
|
runStartLocks = new Map();
|
|
@@ -28,7 +30,7 @@ export class TriggerRuntimeScheduler {
|
|
|
28
30
|
eventSource;
|
|
29
31
|
initialized = false;
|
|
30
32
|
stopping = false;
|
|
31
|
-
constructor(manager, state, audit, scriptExecutor, feedback, daemonChannel, runtime, eventBus) {
|
|
33
|
+
constructor(manager, state, audit, scriptExecutor, feedback, daemonChannel, runtime, eventBus, targetSessionLocks = new TargetSessionLockRegistry()) {
|
|
32
34
|
this.manager = manager;
|
|
33
35
|
this.state = state;
|
|
34
36
|
this.audit = audit;
|
|
@@ -37,6 +39,7 @@ export class TriggerRuntimeScheduler {
|
|
|
37
39
|
this.daemonChannel = daemonChannel;
|
|
38
40
|
this.runtime = runtime;
|
|
39
41
|
this.eventBus = eventBus;
|
|
42
|
+
this.targetSessionLocks = targetSessionLocks;
|
|
40
43
|
if (eventBus) {
|
|
41
44
|
this.eventSource = new TriggerEventSource(eventBus, (triggerId, event) => {
|
|
42
45
|
void this.fireEventTrigger(triggerId, event);
|
|
@@ -391,6 +394,31 @@ export class TriggerRuntimeScheduler {
|
|
|
391
394
|
return { ok: false, runId, triggerId: definition.id, status: 'failed', reason: 'stopped' };
|
|
392
395
|
}
|
|
393
396
|
const source = this.buildSourceInfo(definition.source, firedAt, payload.scheduledAt, payload.payload);
|
|
397
|
+
try {
|
|
398
|
+
validateConcurrencyCombination(definition.execution, definition.reliability);
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
402
|
+
const audit = this.buildAudit({
|
|
403
|
+
definition,
|
|
404
|
+
runId,
|
|
405
|
+
startedAt: firedAt,
|
|
406
|
+
status: 'failed',
|
|
407
|
+
reason: 'invalid_execution_configuration',
|
|
408
|
+
source,
|
|
409
|
+
script: null,
|
|
410
|
+
reply: null,
|
|
411
|
+
feedback: null,
|
|
412
|
+
effects: [],
|
|
413
|
+
error: { code: 'invalid_execution_configuration', message },
|
|
414
|
+
causation: payload.causation,
|
|
415
|
+
});
|
|
416
|
+
if (!payload.dryRun) {
|
|
417
|
+
this.audit.write(audit);
|
|
418
|
+
this.publishTriggerRunOutcome(definition, audit);
|
|
419
|
+
}
|
|
420
|
+
return { ok: false, runId, triggerId: definition.id, status: 'failed', reason: 'invalid_execution_configuration', audit, error: message };
|
|
421
|
+
}
|
|
394
422
|
const executionAuthorization = this.runtime.authorizeExecution?.(definition);
|
|
395
423
|
if (executionAuthorization && !executionAuthorization.allowed) {
|
|
396
424
|
const reason = executionAuthorization.reason || 'execution_authorization_denied';
|
|
@@ -455,6 +483,7 @@ export class TriggerRuntimeScheduler {
|
|
|
455
483
|
definitionRevision: definitionRevision(definition),
|
|
456
484
|
source,
|
|
457
485
|
resumeCount: 0,
|
|
486
|
+
attempts: [],
|
|
458
487
|
events: [{ seq: 0, event: 'run.started', ts: startedAt }],
|
|
459
488
|
};
|
|
460
489
|
if (!payload.dryRun) {
|
|
@@ -597,6 +626,7 @@ export class TriggerRuntimeScheduler {
|
|
|
597
626
|
anomalies: executionAnomalies,
|
|
598
627
|
effects: feedbackResult.effects,
|
|
599
628
|
error: feedbackResult.error,
|
|
629
|
+
conflictRunId: feedbackResult.conflictRunId,
|
|
600
630
|
causation,
|
|
601
631
|
});
|
|
602
632
|
if (!payload.dryRun) {
|
|
@@ -680,7 +710,7 @@ export class TriggerRuntimeScheduler {
|
|
|
680
710
|
let attempt = 1;
|
|
681
711
|
let anomalies = [];
|
|
682
712
|
try {
|
|
683
|
-
let execution = await this.
|
|
713
|
+
let execution = await this.runRecordedExecutionAttempt(definition, runId, firedAt, sourcePayload, signal, dryRun, causation, attempt);
|
|
684
714
|
anomalies = accumulateTriggerExecutionAnomalies(anomalies, mergeTriggerExecutionAnomalies(execution.reply?.anomalies, drainTriggerExecutionAnomalies(runId)));
|
|
685
715
|
onAnomalies?.(anomalies);
|
|
686
716
|
for (let retryCount = 0; shouldRetryExecutionResult(execution) && retryCount < maxAttempts && !signal.aborted; retryCount++) {
|
|
@@ -688,7 +718,7 @@ export class TriggerRuntimeScheduler {
|
|
|
688
718
|
if (!backoffCompleted || signal.aborted)
|
|
689
719
|
break;
|
|
690
720
|
attempt += 1;
|
|
691
|
-
execution = await this.
|
|
721
|
+
execution = await this.runRecordedExecutionAttempt(definition, runId, firedAt, sourcePayload, signal, dryRun, causation, attempt, retryContextFromExecution(attempt - 1, `${runId}:attempt:${attempt - 1}`, execution));
|
|
692
722
|
anomalies = accumulateTriggerExecutionAnomalies(anomalies, mergeTriggerExecutionAnomalies(execution.reply?.anomalies, drainTriggerExecutionAnomalies(runId)));
|
|
693
723
|
onAnomalies?.(anomalies);
|
|
694
724
|
}
|
|
@@ -703,7 +733,34 @@ export class TriggerRuntimeScheduler {
|
|
|
703
733
|
throw new TriggerExecutionFailure(error, anomalies);
|
|
704
734
|
}
|
|
705
735
|
}
|
|
706
|
-
async
|
|
736
|
+
async runRecordedExecutionAttempt(definition, runId, firedAt, sourcePayload, signal, dryRun, causation, attempt, previousAttempt) {
|
|
737
|
+
const attemptId = `${runId}:attempt:${attempt}`;
|
|
738
|
+
if (!dryRun) {
|
|
739
|
+
this.state.startExecutionAttempt?.(definition.id, runId, {
|
|
740
|
+
attempt,
|
|
741
|
+
attemptId,
|
|
742
|
+
startedAt: Date.now(),
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
try {
|
|
746
|
+
const execution = await this.runExecutionAttempt(definition, runId, firedAt, sourcePayload, signal, dryRun, causation, attempt, previousAttempt);
|
|
747
|
+
if (!dryRun) {
|
|
748
|
+
const summary = attemptSummaryFromExecution(attempt, attemptId, execution);
|
|
749
|
+
this.state.completeExecutionAttempt?.(definition.id, runId, attemptId, summary);
|
|
750
|
+
}
|
|
751
|
+
return execution;
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
if (!dryRun) {
|
|
755
|
+
this.state.completeExecutionAttempt?.(definition.id, runId, attemptId, {
|
|
756
|
+
status: 'failed',
|
|
757
|
+
error: errorSummary(error, 'execution_exception'),
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
async runExecutionAttempt(definition, runId, firedAt, sourcePayload, signal, dryRun, causation, attempt = 1, previousAttempt) {
|
|
707
764
|
if (signal.aborted) {
|
|
708
765
|
return {
|
|
709
766
|
script: null,
|
|
@@ -735,12 +792,15 @@ export class TriggerRuntimeScheduler {
|
|
|
735
792
|
reply,
|
|
736
793
|
};
|
|
737
794
|
}
|
|
738
|
-
const
|
|
795
|
+
const originalPrompt = renderTemplate(definition.execution.prompt ?? '', {
|
|
739
796
|
trigger: definition,
|
|
740
797
|
timestamp: firedAt,
|
|
741
798
|
event: sourcePayload,
|
|
742
799
|
source: { type: definition.source.type, payload: sourcePayload },
|
|
743
800
|
});
|
|
801
|
+
const prompt = previousAttempt
|
|
802
|
+
? renderRetryContinuationPrompt(originalPrompt, previousAttempt)
|
|
803
|
+
: originalPrompt;
|
|
744
804
|
const processing = {
|
|
745
805
|
mode: definition.execution.type,
|
|
746
806
|
renderedTextHash: sha256(prompt),
|
|
@@ -756,6 +816,20 @@ export class TriggerRuntimeScheduler {
|
|
|
756
816
|
dryRun,
|
|
757
817
|
causation,
|
|
758
818
|
signal,
|
|
819
|
+
onSessionResolved: async (session) => {
|
|
820
|
+
this.state.startExecutionAttempt?.(definition.id, runId, {
|
|
821
|
+
attempt,
|
|
822
|
+
attemptId: `${runId}:attempt:${attempt}`,
|
|
823
|
+
startedAt: Date.now(),
|
|
824
|
+
executionSessionId: session.id,
|
|
825
|
+
});
|
|
826
|
+
// runExecution is also used by focused unit tests without a live
|
|
827
|
+
// scheduler runtime. Production calls always have a RunningRun.
|
|
828
|
+
if (!this.running.get(definition.id)?.has(runId)) {
|
|
829
|
+
return { kind: 'acquired', release: () => undefined };
|
|
830
|
+
}
|
|
831
|
+
return await this.acquireTargetSessionLock(definition, runId, session.id);
|
|
832
|
+
},
|
|
759
833
|
});
|
|
760
834
|
return {
|
|
761
835
|
script: null,
|
|
@@ -792,19 +866,13 @@ export class TriggerRuntimeScheduler {
|
|
|
792
866
|
const attemptId = `${runId}:attempt:${attempt}`;
|
|
793
867
|
const executionDeadlineAt = this.daemonExecutionDeadlineAt(Date.now());
|
|
794
868
|
const executionSession = await this.daemonChannel.getOrCreateConversationSession(definition, this.runtime.projectPath, runtimeBaseagent, runId, attemptId);
|
|
795
|
-
const activeRun = this.state.
|
|
869
|
+
const activeRun = this.state.startExecutionAttempt?.(definition.id, runId, {
|
|
796
870
|
attempt,
|
|
797
871
|
attemptId,
|
|
872
|
+
startedAt: Date.now(),
|
|
798
873
|
executionSessionId: executionSession.id,
|
|
799
874
|
executionDeadlineAt,
|
|
800
875
|
});
|
|
801
|
-
this.state.appendEvent?.(definition.id, runId, {
|
|
802
|
-
event: 'execution.attempt.started',
|
|
803
|
-
ts: Date.now(),
|
|
804
|
-
attempt,
|
|
805
|
-
attemptId,
|
|
806
|
-
executionSessionId: executionSession.id,
|
|
807
|
-
});
|
|
808
876
|
const daemonReply = await this.daemonChannel.converse(prompt, {
|
|
809
877
|
trigger: definition,
|
|
810
878
|
runId,
|
|
@@ -869,6 +937,26 @@ export class TriggerRuntimeScheduler {
|
|
|
869
937
|
signal,
|
|
870
938
|
});
|
|
871
939
|
}
|
|
940
|
+
async acquireTargetSessionLock(definition, runId, sessionId) {
|
|
941
|
+
const concurrency = definition.reliability.concurrency;
|
|
942
|
+
if (concurrency === 'allow') {
|
|
943
|
+
throw new Error('target_session concurrency=allow must be rejected before session dispatch');
|
|
944
|
+
}
|
|
945
|
+
const runtime = this.running.get(definition.id)?.get(runId);
|
|
946
|
+
if (!runtime) {
|
|
947
|
+
throw new Error(`target session lock requested for non-running Trigger run: ${definition.id}/${runId}`);
|
|
948
|
+
}
|
|
949
|
+
return await this.targetSessionLocks.acquire(sessionId, concurrency, {
|
|
950
|
+
triggerId: definition.id,
|
|
951
|
+
runId,
|
|
952
|
+
replace: async () => {
|
|
953
|
+
await this.terminateRun(runId, runtime, {
|
|
954
|
+
reason: 'replaced',
|
|
955
|
+
message: `target session run replaced: trigger=${definition.id} run=${runId}`,
|
|
956
|
+
});
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
}
|
|
872
960
|
async checkConcurrency(definition, nextRunId, source) {
|
|
873
961
|
const runs = this.running.get(definition.id);
|
|
874
962
|
if (!runs || runs.size === 0 || definition.reliability.concurrency === 'allow')
|
|
@@ -1118,7 +1206,16 @@ export class TriggerRuntimeScheduler {
|
|
|
1118
1206
|
async runRecoveredExecution(definition, runtime, session) {
|
|
1119
1207
|
const { run, source, controller, causation } = runtime;
|
|
1120
1208
|
let attempt = run.attempt;
|
|
1209
|
+
const attemptId = run.attemptId ?? `${run.runId}:attempt:${attempt}`;
|
|
1210
|
+
this.state.startExecutionAttempt(definition.id, run.runId, {
|
|
1211
|
+
attempt,
|
|
1212
|
+
attemptId,
|
|
1213
|
+
startedAt: run.startedAt,
|
|
1214
|
+
executionSessionId: session.id,
|
|
1215
|
+
executionDeadlineAt: run.executionDeadlineAt,
|
|
1216
|
+
});
|
|
1121
1217
|
let execution = await this.runRecoveredExecutionAttempt(definition, runtime, session);
|
|
1218
|
+
this.state.completeExecutionAttempt(definition.id, run.runId, attemptId, attemptSummaryFromExecution(attempt, attemptId, execution));
|
|
1122
1219
|
let anomalies = accumulateTriggerExecutionAnomalies([], mergeTriggerExecutionAnomalies(execution.reply?.anomalies, drainTriggerExecutionAnomalies(run.runId)));
|
|
1123
1220
|
runtime.anomalies = anomalies;
|
|
1124
1221
|
const maxAttempt = 1 + (definition.execution.onError === 'retry'
|
|
@@ -1129,7 +1226,7 @@ export class TriggerRuntimeScheduler {
|
|
|
1129
1226
|
if (!backoffCompleted || controller.signal.aborted)
|
|
1130
1227
|
break;
|
|
1131
1228
|
attempt += 1;
|
|
1132
|
-
execution = await this.
|
|
1229
|
+
execution = await this.runRecordedExecutionAttempt(definition, run.runId, source.firedAt, source.payload, controller.signal, false, causation, attempt, retryContextFromExecution(attempt - 1, `${run.runId}:attempt:${attempt - 1}`, execution));
|
|
1133
1230
|
anomalies = accumulateTriggerExecutionAnomalies(anomalies, mergeTriggerExecutionAnomalies(execution.reply?.anomalies, drainTriggerExecutionAnomalies(run.runId)));
|
|
1134
1231
|
runtime.anomalies = anomalies;
|
|
1135
1232
|
}
|
|
@@ -1356,6 +1453,12 @@ export class TriggerRuntimeScheduler {
|
|
|
1356
1453
|
}
|
|
1357
1454
|
buildAudit(input) {
|
|
1358
1455
|
const reply = summarizeReply(input.reply, { omitText: input.script?.result !== undefined });
|
|
1456
|
+
const activeRun = this.state.list?.(input.definition.id).find(run => run.runId === input.runId);
|
|
1457
|
+
const attempts = activeRun?.attempts?.map(attempt => ({
|
|
1458
|
+
...attempt,
|
|
1459
|
+
...(attempt.error ? { error: { ...attempt.error } } : {}),
|
|
1460
|
+
}));
|
|
1461
|
+
const finalAttempt = attempts?.at(-1);
|
|
1359
1462
|
return {
|
|
1360
1463
|
runId: input.runId,
|
|
1361
1464
|
triggerId: input.definition.id,
|
|
@@ -1365,6 +1468,9 @@ export class TriggerRuntimeScheduler {
|
|
|
1365
1468
|
status: input.status,
|
|
1366
1469
|
reason: input.reason,
|
|
1367
1470
|
conflictRunId: input.conflictRunId,
|
|
1471
|
+
...(activeRun?.executionSessionId ? { executionSessionId: activeRun.executionSessionId } : {}),
|
|
1472
|
+
...(attempts?.length ? { attempts } : {}),
|
|
1473
|
+
...(finalAttempt ? { finalAttempt: finalAttempt.attempt } : {}),
|
|
1368
1474
|
definition: {
|
|
1369
1475
|
schemaVersion: input.definition.$schema_version,
|
|
1370
1476
|
revision: definitionRevision(input.definition),
|
|
@@ -1522,6 +1628,10 @@ export class TriggerRuntimeScheduler {
|
|
|
1522
1628
|
this.removeRunning(triggerId, runId);
|
|
1523
1629
|
return;
|
|
1524
1630
|
}
|
|
1631
|
+
// terminateRun owns the terminal audit and must retain active attempt
|
|
1632
|
+
// snapshots until its completion barrier has settled.
|
|
1633
|
+
if (runtime?.termination)
|
|
1634
|
+
return;
|
|
1525
1635
|
this.finishRun(triggerId, runId);
|
|
1526
1636
|
}
|
|
1527
1637
|
suspendedRuntimeResult(runtime, triggerId, runId) {
|
|
@@ -1787,6 +1897,65 @@ function errorReply(runId, durationMs, reason, text) {
|
|
|
1787
1897
|
},
|
|
1788
1898
|
};
|
|
1789
1899
|
}
|
|
1900
|
+
function attemptSummaryFromExecution(attempt, attemptId, execution) {
|
|
1901
|
+
if (execution.feedbackResult) {
|
|
1902
|
+
const failed = execution.feedbackResult.status !== 'completed';
|
|
1903
|
+
return {
|
|
1904
|
+
status: failed ? 'failed' : 'completed',
|
|
1905
|
+
...(execution.feedbackResult.error
|
|
1906
|
+
? { error: execution.feedbackResult.error }
|
|
1907
|
+
: failed
|
|
1908
|
+
? { error: {
|
|
1909
|
+
code: execution.feedbackResult.reason ?? 'target_session_failed',
|
|
1910
|
+
message: execution.feedbackResult.reason ?? 'target session execution did not start',
|
|
1911
|
+
} }
|
|
1912
|
+
: {}),
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
const reply = execution.reply;
|
|
1916
|
+
if (!reply) {
|
|
1917
|
+
return { status: 'failed', error: { code: 'execution_missing_reply', message: 'execution completed without a reply' } };
|
|
1918
|
+
}
|
|
1919
|
+
switch (reply.outcome) {
|
|
1920
|
+
case 'success': return { status: 'completed' };
|
|
1921
|
+
case 'noop': return { status: 'noop' };
|
|
1922
|
+
case 'interrupted': return { status: 'interrupted', error: replyErrorSummary(reply, 'interrupted') };
|
|
1923
|
+
case 'timeout': return { status: 'timeout', error: replyErrorSummary(reply, 'timeout') };
|
|
1924
|
+
case 'error': return { status: 'failed', error: replyErrorSummary(reply, 'execution_error') };
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
function retryContextFromExecution(attempt, attemptId, execution) {
|
|
1928
|
+
return {
|
|
1929
|
+
attempt,
|
|
1930
|
+
attemptId,
|
|
1931
|
+
...attemptSummaryFromExecution(attempt, attemptId, execution),
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
function replyErrorSummary(reply, fallbackCode) {
|
|
1935
|
+
return {
|
|
1936
|
+
code: reply.error?.reason || fallbackCode,
|
|
1937
|
+
message: previewText(reply.error?.text || `Trigger attempt ${fallbackCode}`, 600),
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
function errorSummary(error, fallbackCode) {
|
|
1941
|
+
const cause = error instanceof TriggerExecutionFailure ? error.cause : error;
|
|
1942
|
+
return {
|
|
1943
|
+
code: fallbackCode,
|
|
1944
|
+
message: previewText(cause instanceof Error ? cause.message : String(cause), 600),
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
function renderRetryContinuationPrompt(originalPrompt, previous) {
|
|
1948
|
+
const error = previous.error
|
|
1949
|
+
? `${previous.error.code}: ${previous.error.message}`
|
|
1950
|
+
: previous.status;
|
|
1951
|
+
return [
|
|
1952
|
+
'这是同一个 Trigger run 的恢复执行,不是新任务。',
|
|
1953
|
+
`上一 attempt 为 ${previous.attempt}(${previous.attemptId}),结果:${error}。`,
|
|
1954
|
+
'请先检查当前会话已有上下文、工具结果和已完成步骤,只继续未完成的工作。',
|
|
1955
|
+
'原始任务:',
|
|
1956
|
+
originalPrompt,
|
|
1957
|
+
].join('\n');
|
|
1958
|
+
}
|
|
1790
1959
|
function summarizeScript(script) {
|
|
1791
1960
|
if (!script)
|
|
1792
1961
|
return null;
|
|
@@ -1833,6 +2002,7 @@ function summarizeReply(reply, opts = {}) {
|
|
|
1833
2002
|
durationMs: reply.meta.durationMs,
|
|
1834
2003
|
numTurns: reply.meta.numTurns,
|
|
1835
2004
|
tokenUsage: reply.meta.tokenUsage,
|
|
2005
|
+
contextRecoveryAttempts: reply.meta.contextRecoveryAttempts,
|
|
1836
2006
|
toolCallCount: reply.meta.toolCallCount,
|
|
1837
2007
|
};
|
|
1838
2008
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide ownership for target sessions. Schedulers are created per
|
|
3
|
+
* agent, so per-scheduler maps cannot protect a session shared by triggers.
|
|
4
|
+
*/
|
|
5
|
+
export class TargetSessionLockRegistry {
|
|
6
|
+
owners = new Map();
|
|
7
|
+
mutations = new Map();
|
|
8
|
+
async acquire(sessionId, concurrency, owner) {
|
|
9
|
+
return await this.withMutation(sessionId, async () => {
|
|
10
|
+
const current = this.owners.get(sessionId);
|
|
11
|
+
if (!current)
|
|
12
|
+
return this.claim(sessionId, owner);
|
|
13
|
+
if (concurrency === 'forbid') {
|
|
14
|
+
return {
|
|
15
|
+
kind: 'conflict',
|
|
16
|
+
conflictTriggerId: current.triggerId,
|
|
17
|
+
conflictRunId: current.runId,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
await current.replace();
|
|
21
|
+
const remaining = this.owners.get(sessionId);
|
|
22
|
+
if (remaining) {
|
|
23
|
+
return {
|
|
24
|
+
kind: 'conflict',
|
|
25
|
+
conflictTriggerId: remaining.triggerId,
|
|
26
|
+
conflictRunId: remaining.runId,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return this.claim(sessionId, owner);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
claim(sessionId, owner) {
|
|
33
|
+
this.owners.set(sessionId, owner);
|
|
34
|
+
let released = false;
|
|
35
|
+
return {
|
|
36
|
+
kind: 'acquired',
|
|
37
|
+
release: () => {
|
|
38
|
+
if (released)
|
|
39
|
+
return;
|
|
40
|
+
released = true;
|
|
41
|
+
if (this.owners.get(sessionId) === owner)
|
|
42
|
+
this.owners.delete(sessionId);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async withMutation(sessionId, action) {
|
|
47
|
+
const previous = this.mutations.get(sessionId) ?? Promise.resolve();
|
|
48
|
+
let releaseCurrent;
|
|
49
|
+
const current = new Promise(resolve => { releaseCurrent = resolve; });
|
|
50
|
+
const tail = previous.then(() => current);
|
|
51
|
+
this.mutations.set(sessionId, tail);
|
|
52
|
+
await previous;
|
|
53
|
+
try {
|
|
54
|
+
return await action();
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
releaseCurrent();
|
|
58
|
+
if (this.mutations.get(sessionId) === tail)
|
|
59
|
+
this.mutations.delete(sessionId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/trigger/state.js
CHANGED
|
@@ -48,6 +48,70 @@ export class TriggerRunStateStore {
|
|
|
48
48
|
this.write(triggerId, active);
|
|
49
49
|
return run;
|
|
50
50
|
}
|
|
51
|
+
startExecutionAttempt(triggerId, runId, input) {
|
|
52
|
+
const active = this.read(triggerId);
|
|
53
|
+
const run = active.runs[runId];
|
|
54
|
+
if (!run)
|
|
55
|
+
return undefined;
|
|
56
|
+
const attempts = run.attempts ?? (run.attempts = []);
|
|
57
|
+
const existing = attempts.find(attempt => attempt.attemptId === input.attemptId);
|
|
58
|
+
if (existing) {
|
|
59
|
+
if (input.executionSessionId)
|
|
60
|
+
existing.executionSessionId = input.executionSessionId;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
attempts.push({
|
|
64
|
+
attempt: input.attempt,
|
|
65
|
+
attemptId: input.attemptId,
|
|
66
|
+
startedAt: input.startedAt,
|
|
67
|
+
status: 'running',
|
|
68
|
+
...(input.executionSessionId ? { executionSessionId: input.executionSessionId } : {}),
|
|
69
|
+
});
|
|
70
|
+
run.events.push({
|
|
71
|
+
seq: run.events.length,
|
|
72
|
+
event: 'execution.attempt.started',
|
|
73
|
+
ts: input.startedAt,
|
|
74
|
+
attempt: input.attempt,
|
|
75
|
+
attemptId: input.attemptId,
|
|
76
|
+
...(input.executionSessionId ? { executionSessionId: input.executionSessionId } : {}),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
run.attempt = input.attempt;
|
|
80
|
+
run.attemptId = input.attemptId;
|
|
81
|
+
if (input.executionSessionId)
|
|
82
|
+
run.executionSessionId = input.executionSessionId;
|
|
83
|
+
if (input.executionDeadlineAt !== undefined)
|
|
84
|
+
run.executionDeadlineAt = input.executionDeadlineAt;
|
|
85
|
+
this.write(triggerId, active);
|
|
86
|
+
return run;
|
|
87
|
+
}
|
|
88
|
+
completeExecutionAttempt(triggerId, runId, attemptId, result) {
|
|
89
|
+
const active = this.read(triggerId);
|
|
90
|
+
const run = active.runs[runId];
|
|
91
|
+
if (!run)
|
|
92
|
+
return undefined;
|
|
93
|
+
const attempt = run.attempts?.find(item => item.attemptId === attemptId);
|
|
94
|
+
if (!attempt || attempt.status !== 'running')
|
|
95
|
+
return run;
|
|
96
|
+
const finishedAt = result.finishedAt ?? Date.now();
|
|
97
|
+
attempt.status = result.status;
|
|
98
|
+
attempt.finishedAt = finishedAt;
|
|
99
|
+
if (result.error)
|
|
100
|
+
attempt.error = result.error;
|
|
101
|
+
run.events.push({
|
|
102
|
+
seq: run.events.length,
|
|
103
|
+
event: result.status === 'completed' || result.status === 'noop'
|
|
104
|
+
? 'execution.attempt.completed'
|
|
105
|
+
: 'execution.attempt.failed',
|
|
106
|
+
ts: finishedAt,
|
|
107
|
+
attempt: attempt.attempt,
|
|
108
|
+
attemptId,
|
|
109
|
+
status: result.status,
|
|
110
|
+
...(result.error ? { error: result.error } : {}),
|
|
111
|
+
});
|
|
112
|
+
this.write(triggerId, active);
|
|
113
|
+
return run;
|
|
114
|
+
}
|
|
51
115
|
remove(triggerId, runId) {
|
|
52
116
|
const active = this.read(triggerId);
|
|
53
117
|
delete active.runs[runId];
|
|
@@ -29,6 +29,35 @@ export function normalizeTriggerDefinition(input, opts = {}) {
|
|
|
29
29
|
export function validateTriggerDefinition(definition) {
|
|
30
30
|
normalizeTriggerDefinition(definition);
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Keep legacy `per_run` definitions byte-stable while giving runtime code one
|
|
34
|
+
* canonical representation for session selection and validation.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizeExecutionMode(execution) {
|
|
37
|
+
if (execution.type !== 'trigger_session')
|
|
38
|
+
return { type: execution.type };
|
|
39
|
+
return {
|
|
40
|
+
type: execution.type,
|
|
41
|
+
thread: execution.thread === 'per_run' || execution.thread === 'by_run'
|
|
42
|
+
? 'by_run'
|
|
43
|
+
: 'by_trigger',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Validate the run-concurrency policy against the session ownership model. */
|
|
47
|
+
export function validateConcurrencyCombination(execution, reliability) {
|
|
48
|
+
// Scripts do not share a model session, so all run-level policies remain valid.
|
|
49
|
+
if (execution.type === 'script')
|
|
50
|
+
return;
|
|
51
|
+
if (reliability.concurrency === 'allow') {
|
|
52
|
+
const mode = normalizeExecutionMode(execution);
|
|
53
|
+
if (mode.type === 'target_session') {
|
|
54
|
+
throw new Error('invalid Trigger concurrency: execution.type=target_session, concurrency=allow; allowed values are forbid or replace');
|
|
55
|
+
}
|
|
56
|
+
if (mode.thread === 'by_trigger') {
|
|
57
|
+
throw new Error('invalid Trigger concurrency: execution.type=trigger_session, thread=by_trigger, concurrency=allow; allowed values are forbid or replace');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
32
61
|
export function definitionRevision(definition) {
|
|
33
62
|
const stable = stableStringify(definition);
|
|
34
63
|
return `sha256:${sha256(stable)}`;
|
|
@@ -381,8 +410,8 @@ function normalizeExecutionThread(value) {
|
|
|
381
410
|
const thread = optionalString(value);
|
|
382
411
|
if (thread === undefined)
|
|
383
412
|
return undefined;
|
|
384
|
-
if (thread !== 'per_run' && thread !== 'by_trigger') {
|
|
385
|
-
throw new Error('execution.thread must be per_run or by_trigger');
|
|
413
|
+
if (thread !== 'per_run' && thread !== 'by_run' && thread !== 'by_trigger') {
|
|
414
|
+
throw new Error('execution.thread must be per_run, by_run, or by_trigger');
|
|
386
415
|
}
|
|
387
416
|
return thread;
|
|
388
417
|
}
|
|
@@ -572,6 +601,7 @@ function validateTriggerSemantics(definition) {
|
|
|
572
601
|
if (execution.type !== 'script' && (definition.feedback.onReply || definition.feedback.onNoop || definition.feedback.onFailure)) {
|
|
573
602
|
throw new Error('feedback.onReply/onNoop/onFailure are only allowed when execution.type=script');
|
|
574
603
|
}
|
|
604
|
+
validateConcurrencyCombination(execution, definition.reliability);
|
|
575
605
|
}
|
|
576
606
|
function generateTriggerId() {
|
|
577
607
|
return `trig_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
package/dist/utils/aid-bind.js
CHANGED
|
@@ -291,7 +291,7 @@ function resolveBaseagentInfo(config) {
|
|
|
291
291
|
return { active_baseagent, model, effort };
|
|
292
292
|
}
|
|
293
293
|
function pickFirstBaseagent(baseagents) {
|
|
294
|
-
for (const name of ['claude', 'codex', 'gemini']) {
|
|
294
|
+
for (const name of ['claude', 'codex', 'gemini', 'ecagent']) {
|
|
295
295
|
if (baseagents[name] !== undefined)
|
|
296
296
|
return name;
|
|
297
297
|
}
|
|
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'url';
|
|
|
3
3
|
import { execFileSync, execFile, spawn, spawnSync } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import fs from 'fs';
|
|
6
|
+
import { getProcessStartTime } from './process-introspect.js';
|
|
6
7
|
const execFileAsync = promisify(execFile);
|
|
7
8
|
export const isWindows = process.platform === 'win32';
|
|
8
9
|
const ENCODE_PATH_MAX = 200;
|
|
@@ -133,10 +134,15 @@ export function getProcessInfo(pid) {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
else {
|
|
136
|
-
|
|
137
|
+
// procps `etimes` can overflow for freshly spawned processes in some
|
|
138
|
+
// container/time-namespace combinations. /proc start time is reliable
|
|
139
|
+
// on Linux and is already used for PID reuse protection elsewhere.
|
|
140
|
+
const startedAt = process.platform === 'linux' ? getProcessStartTime(pid) : null;
|
|
141
|
+
const uptime = startedAt !== null
|
|
142
|
+
? formatUptime(Math.max(0, Math.floor((Date.now() - startedAt) / 1000)))
|
|
143
|
+
: formatUptime(parseInt(execFileSync('ps', ['-p', String(pid), '-o', 'etimes='], { encoding: 'utf-8' }).trim(), 10));
|
|
137
144
|
const cpu = execFileSync('ps', ['-p', String(pid), '-o', '%cpu='], { encoding: 'utf-8' }).trim();
|
|
138
145
|
const mem = execFileSync('ps', ['-p', String(pid), '-o', 'rss='], { encoding: 'utf-8' }).trim();
|
|
139
|
-
const uptime = formatUptime(parseInt(etimes, 10));
|
|
140
146
|
return { uptime, cpu, memory: mem };
|
|
141
147
|
}
|
|
142
148
|
}
|
|
@@ -54,6 +54,15 @@ const NON_INFRA_TERMINAL_REASONS = new Set([
|
|
|
54
54
|
'aborted_streaming',
|
|
55
55
|
'aborted_tools',
|
|
56
56
|
]);
|
|
57
|
+
/**
|
|
58
|
+
* SDK terminal reasons that mean the current turn was stopped before normal
|
|
59
|
+
* completion. In particular, Claude reports a declined/expired
|
|
60
|
+
* AskUserQuestion as `aborted_tools`; that is a user interaction outcome, not
|
|
61
|
+
* an infrastructure or agent execution failure.
|
|
62
|
+
*/
|
|
63
|
+
export function isAbortTerminalReason(terminalReason) {
|
|
64
|
+
return terminalReason === 'aborted_streaming' || terminalReason === 'aborted_tools';
|
|
65
|
+
}
|
|
57
66
|
export function isInfraError(subtype, terminalReason) {
|
|
58
67
|
// terminalReason 优先级更高(更精确)
|
|
59
68
|
if (terminalReason && NON_INFRA_TERMINAL_REASONS.has(terminalReason)) {
|
|
@@ -8,8 +8,25 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'fs';
|
|
10
10
|
import { spawnSync } from 'child_process';
|
|
11
|
-
|
|
11
|
+
const isWindows = process.platform === 'win32';
|
|
12
12
|
const isMacOS = process.platform === 'darwin';
|
|
13
|
+
function readLinuxClockTicks() {
|
|
14
|
+
if (process.platform !== 'linux')
|
|
15
|
+
return 100;
|
|
16
|
+
try {
|
|
17
|
+
const result = spawnSync('getconf', ['CLK_TCK'], {
|
|
18
|
+
encoding: 'utf-8',
|
|
19
|
+
timeout: 1000,
|
|
20
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
21
|
+
});
|
|
22
|
+
const value = Number.parseInt(result.stdout?.trim() || '', 10);
|
|
23
|
+
return value > 0 ? value : 100;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return 100;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const linuxClockTicks = readLinuxClockTicks();
|
|
13
30
|
/** 容差:2 秒(覆盖 macOS 秒级精度 + 时钟漂移) */
|
|
14
31
|
export const START_TIME_TOLERANCE_MS = 2000;
|
|
15
32
|
/**
|
|
@@ -72,8 +89,7 @@ function getStartTimeLinux(pid) {
|
|
|
72
89
|
}
|
|
73
90
|
if (isNaN(btimeSec))
|
|
74
91
|
return null;
|
|
75
|
-
|
|
76
|
-
return (btimeSec + starttimeJiffies / clkTck) * 1000;
|
|
92
|
+
return (btimeSec + starttimeJiffies / linuxClockTicks) * 1000;
|
|
77
93
|
}
|
|
78
94
|
// ── macOS ──
|
|
79
95
|
function getStartTimeMacOS(pid) {
|
package/dist/utils/welcome.js
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
* - 所有 IM 渠道(Feishu/WeChat/DingTalk/QQBot/WeCom):首次交互时发送给交互用户(被动响应)
|
|
9
9
|
*/
|
|
10
10
|
import fs from 'fs';
|
|
11
|
-
import
|
|
12
|
-
import { resolvePaths } from '../paths.js';
|
|
11
|
+
import { channelStatePath } from '../paths.js';
|
|
13
12
|
import { loadAgent, saveAgent } from '../config-store.js';
|
|
14
13
|
import { withLifecycleForWrite } from '../config/lifecycle.js';
|
|
15
14
|
import { logger } from './logger.js';
|
|
@@ -157,7 +156,7 @@ export class FirstInteractionWelcomeManager {
|
|
|
157
156
|
this.agentAid = agentAid;
|
|
158
157
|
this.channelName = channelName;
|
|
159
158
|
// 每个 channel instance 独立的已欢迎用户记录
|
|
160
|
-
this.greetedUsersFile =
|
|
159
|
+
this.greetedUsersFile = channelStatePath(channelName, 'welcome.jsonl');
|
|
161
160
|
this.loadGreetedUsers();
|
|
162
161
|
}
|
|
163
162
|
/**
|
package/ecagent/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mario Zechner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|