evolcore 0.0.5 → 0.0.7
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 +49 -0
- 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 +88 -8
- package/dist/cli/data-command.js +132 -0
- package/dist/cli/index.js +11 -3
- package/dist/cli/init.js +76 -17
- package/dist/cli/net-check.js +9 -20
- package/dist/cli/restart-monitor.js +25 -2
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg.js +12 -25
- 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/command/command-handler.js +2 -2
- package/dist/core/command/menu-handler.js +11 -9
- package/dist/core/command/role-menu.js +11 -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/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 +283 -59
- package/dist/core/session/session-manager.js +19 -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 +13 -10
- 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/error-utils.js +9 -0
- package/dist/utils/instance-registry.js +2 -2
- 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/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.10.json +370 -0
- 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
|
@@ -20,22 +20,98 @@ function resolveAunSelfAID(selfAID, channelKey) {
|
|
|
20
20
|
return inferred;
|
|
21
21
|
throw new Error('[SessionFsStore] AUN chat directory requires selfAID or an aun channel key.');
|
|
22
22
|
}
|
|
23
|
+
function resolveSessionChannelKey(channelType, selfAID, channelKey) {
|
|
24
|
+
if (channelKey?.trim())
|
|
25
|
+
return channelKey.trim();
|
|
26
|
+
return `${channelType}#${selfAID}#main`;
|
|
27
|
+
}
|
|
28
|
+
function encodeChannelSessionComponent(value) {
|
|
29
|
+
// encodeURIComponent leaves `*` untouched, but it is illegal in a Windows
|
|
30
|
+
// filename. A trailing dot is also rejected by Windows filesystems.
|
|
31
|
+
const encoded = encodeURIComponent(value).replace(/\*/g, '%2A');
|
|
32
|
+
return encoded.endsWith('.') ? `${encoded.slice(0, -1)}%2E` : encoded;
|
|
33
|
+
}
|
|
34
|
+
const MAX_CHANNEL_SESSION_KEY_BYTES = 255;
|
|
35
|
+
function formatChannelSessionKeyParts(channelType, channelName, channelId) {
|
|
36
|
+
if (!channelType || !channelName || !channelId) {
|
|
37
|
+
throw new Error('[SessionFsStore] channel session key components must not be empty.');
|
|
38
|
+
}
|
|
39
|
+
const key = [channelType, channelName, channelId]
|
|
40
|
+
.map(encodeChannelSessionComponent)
|
|
41
|
+
.join('#');
|
|
42
|
+
const bytes = Buffer.byteLength(key, 'utf8');
|
|
43
|
+
if (bytes > MAX_CHANNEL_SESSION_KEY_BYTES) {
|
|
44
|
+
throw new Error(`[SessionFsStore] channel session key is ${bytes} bytes; filesystem limit is ${MAX_CHANNEL_SESSION_KEY_BYTES} bytes.`);
|
|
45
|
+
}
|
|
46
|
+
return key;
|
|
47
|
+
}
|
|
23
48
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
49
|
+
* Session 的磁盘路由键。它将实例名和会话路由 ID 合并为一个目录段;
|
|
50
|
+
* Agent 身份由父目录 `agents/<aid>/` 提供,不能省略这个父目录。
|
|
51
|
+
*
|
|
52
|
+
* This is intentionally distinct from SessionFile.sessionKey, which includes
|
|
53
|
+
* the thread ID and is used as the agent-internal session routing key.
|
|
28
54
|
*/
|
|
29
|
-
export function
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
export function formatChannelSessionKey(channelType, channelId, channelKey) {
|
|
56
|
+
const parts = channelKey?.split('#');
|
|
57
|
+
const channelName = parts?.length === 3 && parts[0] === channelType && parts[2]?.trim()
|
|
58
|
+
? parts[2].trim()
|
|
59
|
+
: 'main';
|
|
60
|
+
return formatChannelSessionKeyParts(channelType, channelName, channelId);
|
|
61
|
+
}
|
|
62
|
+
function parseChannelSessionKeyComponents(key) {
|
|
63
|
+
const parts = key.split('#');
|
|
64
|
+
if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2])
|
|
65
|
+
return undefined;
|
|
66
|
+
try {
|
|
67
|
+
const channelType = decodeURIComponent(parts[0]);
|
|
68
|
+
const channelName = decodeURIComponent(parts[1]);
|
|
69
|
+
const channelId = decodeURIComponent(parts[2]);
|
|
70
|
+
if (!channelType || !channelName || !channelId)
|
|
71
|
+
return undefined;
|
|
72
|
+
return { channelType, channelName, channelId };
|
|
32
73
|
}
|
|
33
|
-
|
|
34
|
-
return
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
35
76
|
}
|
|
36
|
-
return path.join(sessionsDir, channelType, encodeSegment(channelId));
|
|
37
77
|
}
|
|
38
|
-
/**
|
|
78
|
+
/** Parse only the canonical channelSessionKey emitted by the current formatter. */
|
|
79
|
+
export function tryParseChannelSessionKey(key) {
|
|
80
|
+
const parsed = parseChannelSessionKeyComponents(key);
|
|
81
|
+
if (!parsed)
|
|
82
|
+
return undefined;
|
|
83
|
+
try {
|
|
84
|
+
return formatChannelSessionKeyParts(parsed.channelType, parsed.channelName, parsed.channelId) === key
|
|
85
|
+
? parsed
|
|
86
|
+
: undefined;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Parse the immediately preceding v3 encoding for explicit migration only. */
|
|
93
|
+
export function tryParseLegacyChannelSessionKey(key) {
|
|
94
|
+
return parseChannelSessionKeyComponents(key);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 计算 chat 目录的完整路径。
|
|
98
|
+
* - agents/<selfAID>/sessions/<channelSessionKey>/
|
|
99
|
+
*
|
|
100
|
+
* The relation peerKey intentionally is not used here: it omits the channel
|
|
101
|
+
* instance. channelSessionKey retains the configured instance name and the
|
|
102
|
+
* chat route in one path segment.
|
|
103
|
+
*/
|
|
104
|
+
export function chatDirPath(sessionsRoot, channelType, channelId, selfAID, channelKey) {
|
|
105
|
+
const aid = channelType === 'aun'
|
|
106
|
+
? resolveAunSelfAID(selfAID, channelKey)
|
|
107
|
+
: (selfAID?.trim() || trySelfAIDFromChannelKey(channelKey) || '_unknown');
|
|
108
|
+
return path.join(sessionsRoot, encodeSegment(aid), 'sessions', formatChannelSessionKey(channelType, channelId, resolveSessionChannelKey(channelType, aid, channelKey)));
|
|
109
|
+
}
|
|
110
|
+
function trySelfAIDFromChannelKey(channelKey) {
|
|
111
|
+
const parts = channelKey?.split('#');
|
|
112
|
+
return parts?.length === 3 && parts[1]?.trim() ? parts[1].trim() : undefined;
|
|
113
|
+
}
|
|
114
|
+
/** 解码目录段(用于扫描时把目录名还原为逻辑路径键) */
|
|
39
115
|
export function decodeDirSegment(seg) {
|
|
40
116
|
return decodeSegment(seg);
|
|
41
117
|
}
|
|
@@ -143,22 +219,193 @@ export function readAllJsonlLines(filePath) {
|
|
|
143
219
|
}
|
|
144
220
|
}
|
|
145
221
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* - wecom: channelType / channelKey / channelId (3层;兼容旧的2层目录)
|
|
149
|
-
* - 其它: channelType / channelId (2层)
|
|
222
|
+
* Scan the current per-Agent layout:
|
|
223
|
+
* `agents/<aid>/sessions/<channelSessionKey>/`.
|
|
150
224
|
*/
|
|
151
|
-
export function scanChatDirs(
|
|
225
|
+
export function scanChatDirs(sessionsRoot) {
|
|
152
226
|
const results = [];
|
|
153
|
-
let
|
|
227
|
+
let agentEntries;
|
|
154
228
|
try {
|
|
155
|
-
|
|
229
|
+
agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
|
|
156
230
|
}
|
|
157
231
|
catch (e) {
|
|
158
232
|
if (e.code === 'ENOENT')
|
|
159
233
|
return [];
|
|
160
234
|
throw e;
|
|
161
235
|
}
|
|
236
|
+
const hasAgentLayout = agentEntries.some(entry => entry.isDirectory() && (fs.existsSync(path.join(sessionsRoot, entry.name, 'config.json'))
|
|
237
|
+
|| fs.existsSync(path.join(sessionsRoot, entry.name, 'sessions'))));
|
|
238
|
+
if (!hasAgentLayout)
|
|
239
|
+
return [];
|
|
240
|
+
for (const agentEntry of agentEntries) {
|
|
241
|
+
if (!agentEntry.isDirectory())
|
|
242
|
+
continue;
|
|
243
|
+
const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
|
|
244
|
+
let sessionEntries;
|
|
245
|
+
try {
|
|
246
|
+
sessionEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const selfAID = decodeSegment(agentEntry.name);
|
|
252
|
+
for (const sessionEntry of sessionEntries) {
|
|
253
|
+
if (!sessionEntry.isDirectory())
|
|
254
|
+
continue;
|
|
255
|
+
const channelSessionKey = sessionEntry.name;
|
|
256
|
+
const parsed = tryParseChannelSessionKey(channelSessionKey);
|
|
257
|
+
if (!parsed)
|
|
258
|
+
continue;
|
|
259
|
+
const dirPath = path.join(sessionsDir, sessionEntry.name);
|
|
260
|
+
if (!hasSessionPayload(dirPath))
|
|
261
|
+
continue;
|
|
262
|
+
results.push({
|
|
263
|
+
channelType: parsed.channelType,
|
|
264
|
+
selfAID,
|
|
265
|
+
channelId: parsed.channelId,
|
|
266
|
+
channelKey: parsed.channelType === 'daemon'
|
|
267
|
+
? 'daemon'
|
|
268
|
+
: `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
|
|
269
|
+
channelSessionKey,
|
|
270
|
+
dirPath,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
275
|
+
}
|
|
276
|
+
function hasSessionPayload(dirPath) {
|
|
277
|
+
try {
|
|
278
|
+
return fs.readdirSync(dirPath, { withFileTypes: true }).some(entry => (entry.isFile() && (entry.name === 'active.json'
|
|
279
|
+
|| entry.name === 'messages.jsonl'
|
|
280
|
+
|| (entry.name.startsWith('meta_') && entry.name.endsWith('.jsonl')))) || (entry.isDirectory() && entry.name === '_threads'));
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Scan non-canonical single-level v3 keys. The runtime ignores these entries;
|
|
288
|
+
* only the explicit migration planner may copy them into their canonical key.
|
|
289
|
+
*/
|
|
290
|
+
export function scanNonCanonicalAgentChatDirs(sessionsRoot) {
|
|
291
|
+
const results = [];
|
|
292
|
+
let agentEntries;
|
|
293
|
+
try {
|
|
294
|
+
agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
return results;
|
|
298
|
+
}
|
|
299
|
+
for (const agentEntry of agentEntries) {
|
|
300
|
+
if (!agentEntry.isDirectory())
|
|
301
|
+
continue;
|
|
302
|
+
const selfAID = decodeSegment(agentEntry.name);
|
|
303
|
+
const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
|
|
304
|
+
let sessionEntries;
|
|
305
|
+
try {
|
|
306
|
+
sessionEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
for (const sessionEntry of sessionEntries) {
|
|
312
|
+
if (!sessionEntry.isDirectory() || tryParseChannelSessionKey(sessionEntry.name))
|
|
313
|
+
continue;
|
|
314
|
+
const parsed = tryParseLegacyChannelSessionKey(sessionEntry.name);
|
|
315
|
+
const dirPath = path.join(sessionsDir, sessionEntry.name);
|
|
316
|
+
if (!parsed || !hasSessionPayload(dirPath))
|
|
317
|
+
continue;
|
|
318
|
+
results.push({
|
|
319
|
+
...parsed,
|
|
320
|
+
selfAID,
|
|
321
|
+
channelKey: parsed.channelType === 'daemon'
|
|
322
|
+
? 'daemon'
|
|
323
|
+
: `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
|
|
324
|
+
channelSessionKey: sessionEntry.name,
|
|
325
|
+
dirPath,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Scan the immediately preceding per-Agent layout:
|
|
333
|
+
* `agents/<aid>/sessions/<channelKey>/<channelId>/`.
|
|
334
|
+
*
|
|
335
|
+
* The runtime must not use this scanner. It exists solely for the explicit
|
|
336
|
+
* migration that copies the old two-level layout into channelSessionKey.
|
|
337
|
+
*/
|
|
338
|
+
export function scanNestedAgentChatDirs(sessionsRoot) {
|
|
339
|
+
const results = [];
|
|
340
|
+
let agentEntries;
|
|
341
|
+
try {
|
|
342
|
+
agentEntries = fs.readdirSync(sessionsRoot, { withFileTypes: true });
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return results;
|
|
346
|
+
}
|
|
347
|
+
for (const agentEntry of agentEntries) {
|
|
348
|
+
if (!agentEntry.isDirectory())
|
|
349
|
+
continue;
|
|
350
|
+
const selfAID = decodeSegment(agentEntry.name);
|
|
351
|
+
const sessionsDir = path.join(sessionsRoot, agentEntry.name, 'sessions');
|
|
352
|
+
let channelEntries;
|
|
353
|
+
try {
|
|
354
|
+
channelEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
for (const channelEntry of channelEntries) {
|
|
360
|
+
if (!channelEntry.isDirectory())
|
|
361
|
+
continue;
|
|
362
|
+
const channelKey = decodeSegment(channelEntry.name);
|
|
363
|
+
const parts = channelKey.split('#');
|
|
364
|
+
const isDaemon = channelKey === 'daemon';
|
|
365
|
+
const isCurrentKey = parts.length === 3 && parts[1] === selfAID && !!parts[0] && !!parts[2];
|
|
366
|
+
const isReversedLegacyKey = parts.length === 3 && parts[0] === selfAID && !!parts[1] && !!parts[2];
|
|
367
|
+
const isAgentChannel = isCurrentKey || isReversedLegacyKey;
|
|
368
|
+
if (!isDaemon && !isAgentChannel)
|
|
369
|
+
continue;
|
|
370
|
+
const channelDir = path.join(sessionsDir, channelEntry.name);
|
|
371
|
+
// A single-level channelSessionKey can look superficially like a legacy
|
|
372
|
+
// key when an AID happens to match an instance name. Its own active.json
|
|
373
|
+
// proves it is a current chat directory, not a legacy channel parent.
|
|
374
|
+
if (fs.existsSync(path.join(channelDir, 'active.json')))
|
|
375
|
+
continue;
|
|
376
|
+
const channelType = isDaemon ? 'daemon' : (isCurrentKey ? parts[0] : parts[1]);
|
|
377
|
+
let chatEntries;
|
|
378
|
+
try {
|
|
379
|
+
chatEntries = fs.readdirSync(channelDir, { withFileTypes: true });
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
for (const chatEntry of chatEntries) {
|
|
385
|
+
if (!chatEntry.isDirectory())
|
|
386
|
+
continue;
|
|
387
|
+
results.push({
|
|
388
|
+
channelType,
|
|
389
|
+
selfAID,
|
|
390
|
+
channelKey,
|
|
391
|
+
channelId: decodeSegment(chatEntry.name),
|
|
392
|
+
dirPath: path.join(channelDir, chatEntry.name),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
398
|
+
}
|
|
399
|
+
/** Scan the pre-migration data/sessions layout without changing it. */
|
|
400
|
+
export function scanLegacyChatDirs(sessionsDir) {
|
|
401
|
+
const results = [];
|
|
402
|
+
let typeEntries;
|
|
403
|
+
try {
|
|
404
|
+
typeEntries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
return results;
|
|
408
|
+
}
|
|
162
409
|
for (const typeEntry of typeEntries) {
|
|
163
410
|
if (!typeEntry.isDirectory())
|
|
164
411
|
continue;
|
|
@@ -172,7 +419,6 @@ export function scanChatDirs(sessionsDir) {
|
|
|
172
419
|
continue;
|
|
173
420
|
}
|
|
174
421
|
if (channelType === 'aun') {
|
|
175
|
-
// 3-layer: aun/selfAID/channelId
|
|
176
422
|
for (const selfEntry of level2Entries) {
|
|
177
423
|
if (!selfEntry.isDirectory())
|
|
178
424
|
continue;
|
|
@@ -184,19 +430,15 @@ export function scanChatDirs(sessionsDir) {
|
|
|
184
430
|
catch {
|
|
185
431
|
continue;
|
|
186
432
|
}
|
|
187
|
-
for (const chatEntry of chatEntries)
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
selfAID: decodeSegment(selfEntry.name),
|
|
193
|
-
channelId: decodeSegment(chatEntry.name),
|
|
194
|
-
dirPath: path.join(selfDir, chatEntry.name),
|
|
195
|
-
});
|
|
196
|
-
}
|
|
433
|
+
for (const chatEntry of chatEntries)
|
|
434
|
+
if (chatEntry.isDirectory())
|
|
435
|
+
results.push({
|
|
436
|
+
channelType, selfAID: decodeSegment(selfEntry.name), channelId: decodeSegment(chatEntry.name), dirPath: path.join(selfDir, chatEntry.name),
|
|
437
|
+
});
|
|
197
438
|
}
|
|
439
|
+
continue;
|
|
198
440
|
}
|
|
199
|
-
|
|
441
|
+
if (channelType === 'wecom') {
|
|
200
442
|
for (const entry of level2Entries) {
|
|
201
443
|
if (!entry.isDirectory())
|
|
202
444
|
continue;
|
|
@@ -210,43 +452,25 @@ export function scanChatDirs(sessionsDir) {
|
|
|
210
452
|
catch {
|
|
211
453
|
continue;
|
|
212
454
|
}
|
|
213
|
-
for (const chatEntry of chatEntries)
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
selfAID: '',
|
|
219
|
-
channelKey: decoded,
|
|
220
|
-
channelId: decodeSegment(chatEntry.name),
|
|
221
|
-
dirPath: path.join(entryDir, chatEntry.name),
|
|
222
|
-
});
|
|
223
|
-
}
|
|
455
|
+
for (const chatEntry of chatEntries)
|
|
456
|
+
if (chatEntry.isDirectory())
|
|
457
|
+
results.push({
|
|
458
|
+
channelType, selfAID: '', channelKey: decoded, channelId: decodeSegment(chatEntry.name), dirPath: path.join(entryDir, chatEntry.name),
|
|
459
|
+
});
|
|
224
460
|
}
|
|
225
461
|
else {
|
|
226
|
-
results.push({
|
|
227
|
-
channelType,
|
|
228
|
-
selfAID: '',
|
|
229
|
-
channelId: decoded,
|
|
230
|
-
dirPath: entryDir,
|
|
231
|
-
});
|
|
462
|
+
results.push({ channelType, selfAID: '', channelId: decoded, dirPath: entryDir });
|
|
232
463
|
}
|
|
233
464
|
}
|
|
465
|
+
continue;
|
|
234
466
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
for (const chatEntry of level2Entries) {
|
|
238
|
-
if (!chatEntry.isDirectory())
|
|
239
|
-
continue;
|
|
467
|
+
for (const chatEntry of level2Entries)
|
|
468
|
+
if (chatEntry.isDirectory())
|
|
240
469
|
results.push({
|
|
241
|
-
channelType,
|
|
242
|
-
selfAID: '',
|
|
243
|
-
channelId: decodeSegment(chatEntry.name),
|
|
244
|
-
dirPath: path.join(typeDir, chatEntry.name),
|
|
470
|
+
channelType, selfAID: '', channelId: decodeSegment(chatEntry.name), dirPath: path.join(typeDir, chatEntry.name),
|
|
245
471
|
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
472
|
}
|
|
249
|
-
return results;
|
|
473
|
+
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
250
474
|
}
|
|
251
475
|
export function scanMetaFiles(chatDir) {
|
|
252
476
|
try {
|
|
@@ -38,9 +38,6 @@ export class SessionManager {
|
|
|
38
38
|
this.sessionsDir = sessionsDir;
|
|
39
39
|
this.eventBus = eventBus;
|
|
40
40
|
this.identityResolver = identityResolver;
|
|
41
|
-
this.migrateChannelKeyFormat();
|
|
42
|
-
this.migrateUnknownAunDirs();
|
|
43
|
-
this.migrateWecomChannelDirs();
|
|
44
41
|
}
|
|
45
42
|
setIdentityResolver(resolver) {
|
|
46
43
|
this.identityResolver = resolver;
|
|
@@ -379,7 +376,7 @@ export class SessionManager {
|
|
|
379
376
|
logger.error(`[SessionManager] validateSessionFile: no adapter for baseagent=${session.baseagent}, session=${session.id}`);
|
|
380
377
|
return agentSessionId;
|
|
381
378
|
}
|
|
382
|
-
if (adapter.checkExists(session.projectPath, agentSessionId))
|
|
379
|
+
if (adapter.checkExists(session.projectPath, agentSessionId, session.selfAID))
|
|
383
380
|
return agentSessionId;
|
|
384
381
|
logger.warn(`Session file not found for ${session.baseagent}: ${agentSessionId}, clearing session ID`);
|
|
385
382
|
session.agentSessionId = undefined;
|
|
@@ -1316,16 +1313,16 @@ export class SessionManager {
|
|
|
1316
1313
|
});
|
|
1317
1314
|
return session;
|
|
1318
1315
|
}
|
|
1319
|
-
async scanCliSessions(projectPath, baseagent, limit = 10) {
|
|
1316
|
+
async scanCliSessions(projectPath, baseagent, limit = 10, agentAid) {
|
|
1320
1317
|
const adapter = this.getFileAdapter(baseagent);
|
|
1321
1318
|
if (!adapter)
|
|
1322
1319
|
return [];
|
|
1323
|
-
return adapter.scanCliSessions(projectPath, limit);
|
|
1320
|
+
return adapter.scanCliSessions(projectPath, limit, agentAid);
|
|
1324
1321
|
}
|
|
1325
|
-
async listImportableCliSessions(projectPath, baseagent) {
|
|
1322
|
+
async listImportableCliSessions(projectPath, baseagent, agentAid) {
|
|
1326
1323
|
const boundSessionIds = this.listBoundAgentSessionIds(projectPath, baseagent);
|
|
1327
1324
|
const displayLimit = 10;
|
|
1328
|
-
const cliSessions = await this.scanCliSessions(projectPath, baseagent, boundSessionIds.size + displayLimit);
|
|
1325
|
+
const cliSessions = await this.scanCliSessions(projectPath, baseagent, boundSessionIds.size + displayLimit, agentAid);
|
|
1329
1326
|
return cliSessions
|
|
1330
1327
|
.filter(session => !boundSessionIds.has(session.uuid))
|
|
1331
1328
|
.slice(0, displayLimit);
|
|
@@ -1344,25 +1341,25 @@ export class SessionManager {
|
|
|
1344
1341
|
}
|
|
1345
1342
|
return sessionIds;
|
|
1346
1343
|
}
|
|
1347
|
-
checkSessionFileExists(projectPath, agentSessionId, baseagent) {
|
|
1344
|
+
checkSessionFileExists(projectPath, agentSessionId, baseagent, agentAid) {
|
|
1348
1345
|
const adapter = this.getFileAdapter(baseagent);
|
|
1349
1346
|
if (!adapter)
|
|
1350
1347
|
return false;
|
|
1351
|
-
return adapter.checkExists(projectPath, agentSessionId);
|
|
1348
|
+
return adapter.checkExists(projectPath, agentSessionId, agentAid);
|
|
1352
1349
|
}
|
|
1353
|
-
readSessionFirstMessage(projectPath, agentSessionId, baseagent) {
|
|
1350
|
+
readSessionFirstMessage(projectPath, agentSessionId, baseagent, agentAid) {
|
|
1354
1351
|
const adapter = this.getFileAdapter(baseagent);
|
|
1355
1352
|
if (!adapter)
|
|
1356
1353
|
return null;
|
|
1357
|
-
return adapter.readFirstMessage(projectPath, agentSessionId);
|
|
1354
|
+
return adapter.readFirstMessage(projectPath, agentSessionId, agentAid);
|
|
1358
1355
|
}
|
|
1359
|
-
readSessionLastUserMessage(projectPath, agentSessionId, baseagent) {
|
|
1356
|
+
readSessionLastUserMessage(projectPath, agentSessionId, baseagent, agentAid) {
|
|
1360
1357
|
const adapter = this.getFileAdapter(baseagent);
|
|
1361
1358
|
if (!adapter)
|
|
1362
1359
|
return null;
|
|
1363
|
-
return adapter.readLastUserMessage(projectPath, agentSessionId);
|
|
1360
|
+
return adapter.readLastUserMessage(projectPath, agentSessionId, agentAid);
|
|
1364
1361
|
}
|
|
1365
|
-
getSessionFileInfo(projectPath, agentSessionId, baseagent) {
|
|
1362
|
+
getSessionFileInfo(projectPath, agentSessionId, baseagent, agentAid) {
|
|
1366
1363
|
if (!baseagent) {
|
|
1367
1364
|
logger.error(`[SessionManager] getSessionFileInfo: baseagent is empty, returning turns=0. agentSessionId=${agentSessionId}`);
|
|
1368
1365
|
return { turns: 0 };
|
|
@@ -1372,13 +1369,13 @@ export class SessionManager {
|
|
|
1372
1369
|
logger.error(`[SessionManager] getSessionFileInfo: no adapter for baseagent=${baseagent}, returning turns=0`);
|
|
1373
1370
|
return { turns: 0 };
|
|
1374
1371
|
}
|
|
1375
|
-
return adapter.getFileInfo(projectPath, agentSessionId);
|
|
1372
|
+
return adapter.getFileInfo(projectPath, agentSessionId, agentAid);
|
|
1376
1373
|
}
|
|
1377
|
-
async listSdkSessions(projectPath, baseagent) {
|
|
1374
|
+
async listSdkSessions(projectPath, baseagent, agentAid) {
|
|
1378
1375
|
const adapter = this.getFileAdapter(baseagent);
|
|
1379
1376
|
if (!adapter?.listSdkSessions)
|
|
1380
1377
|
return [];
|
|
1381
|
-
return adapter.listSdkSessions(projectPath);
|
|
1378
|
+
return adapter.listSdkSessions(projectPath, agentAid);
|
|
1382
1379
|
}
|
|
1383
1380
|
async getSessionByUuidPrefix(channel, channelId, uuidPrefix) {
|
|
1384
1381
|
const chatDir = this.resolveChatDirSafe(channel, channelId);
|
|
@@ -1391,15 +1388,16 @@ export class SessionManager {
|
|
|
1391
1388
|
}
|
|
1392
1389
|
return matched[0];
|
|
1393
1390
|
}
|
|
1394
|
-
async importCliSession(channel, channelId, projectPath, agentSessionId, baseagent = 'claude') {
|
|
1391
|
+
async importCliSession(channel, channelId, projectPath, agentSessionId, baseagent = 'claude', agentAid) {
|
|
1395
1392
|
const inheritedChatType = this.getActiveChatType(channel, channelId);
|
|
1396
|
-
const
|
|
1393
|
+
const resolvedAgentAid = agentAid || this.deriveChannelIdentity(channel, channelId).selfAID;
|
|
1394
|
+
const fileInfo = this.getSessionFileInfo(projectPath, agentSessionId, baseagent, resolvedAgentAid);
|
|
1397
1395
|
const name = fileInfo.title || `CLI会话-${new Date().toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })}`;
|
|
1398
1396
|
// Derive selfAID and channelType from existing sessions
|
|
1399
1397
|
const existingDir = this.findExistingChatDir(channel, channelId);
|
|
1400
1398
|
const identity = this.deriveChannelIdentity(channel, channelId);
|
|
1401
1399
|
let channelType = identity.channelType;
|
|
1402
|
-
let selfAID = identity.selfAID;
|
|
1400
|
+
let selfAID = identity.selfAID || resolvedAgentAid || '';
|
|
1403
1401
|
if (existingDir) {
|
|
1404
1402
|
const active = readJsonFile(path.join(existingDir, 'active.json'));
|
|
1405
1403
|
if (active) {
|
|
@@ -30,6 +30,7 @@ const BACKEND_SYNTHETIC_CLI_PROMPT_PREFIXES = {
|
|
|
30
30
|
],
|
|
31
31
|
codex: [],
|
|
32
32
|
gemini: [],
|
|
33
|
+
ecagent: [],
|
|
33
34
|
};
|
|
34
35
|
// Claude Code 斜杠命令 / skill / hook 注入的脚手架,经真实会话数据核对,形态包括:
|
|
35
36
|
// - <local-command-caveat> / <local-command-stdout> / <local-command-stderr> 等 local-command 标签族
|
|
@@ -142,9 +142,16 @@ export class SessionTurnCoordinator {
|
|
|
142
142
|
if (evidence.openToolUseIds.length > 0)
|
|
143
143
|
return { ok: false, reason: 'tool_open' };
|
|
144
144
|
const madeProgress = evidence.textCount > 0 || evidence.toolUseCount > 0 || !!evidence.resultText?.trim();
|
|
145
|
-
|
|
145
|
+
// Not every runner can emit input_accepted. Token usage is equally strong
|
|
146
|
+
// evidence that the upstream model consumed the input, even for a silent result.
|
|
147
|
+
const inputConsumed = evidence.inputAccepted
|
|
148
|
+
|| evidence.hasTokenUsage === true
|
|
149
|
+
|| evidence.inputTokens > 0
|
|
150
|
+
|| evidence.outputTokens > 0;
|
|
151
|
+
if (!inputConsumed && !madeProgress)
|
|
146
152
|
return { ok: false, reason: 'input_not_consumed' };
|
|
147
153
|
const emptySuccess = !evidence.explicitNoop
|
|
154
|
+
&& !evidence.hasTokenUsage
|
|
148
155
|
&& evidence.inputTokens === 0
|
|
149
156
|
&& evidence.outputTokens === 0
|
|
150
157
|
&& !madeProgress;
|
|
@@ -14,6 +14,7 @@ export function isSystemControlChannel(channel) {
|
|
|
14
14
|
export function isNonAgentSessionChannel(channel) {
|
|
15
15
|
return channel === 'daemon' || isSystemControlChannel(channel);
|
|
16
16
|
}
|
|
17
|
+
/** Explicit maintenance helper; startup never invokes it. */
|
|
17
18
|
export function cleanupLegacySystemControlSessions(sessionsDir) {
|
|
18
19
|
const removed = [];
|
|
19
20
|
for (const channel of LEGACY_SYSTEM_CONTROL_CHANNELS) {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 【重要】最先加载环境变量,确保后续模块初始化时可用
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { ensureDataDirs, resolvePaths, getPackageRoot, agentMdPath, eckDebugDir } from './paths.js';
|
|
4
|
+
import { ensureDataDirs, resolvePaths, getPackageRoot, agentMdPath, eckDebugDir, daemonControlDir, daemonQueuePath } from './paths.js';
|
|
5
5
|
// 立即加载 .env 文件(在其他模块导入之前)
|
|
6
6
|
try {
|
|
7
7
|
dotenv.config({ path: path.join(resolvePaths().root, '.env') });
|
|
@@ -12,6 +12,7 @@ catch {
|
|
|
12
12
|
import { ClaudeSessionFileAdapter } from './core/session/adapters/claude-session-file-adapter.js';
|
|
13
13
|
import { CodexSessionFileAdapter } from './core/session/adapters/codex-session-file-adapter.js';
|
|
14
14
|
import { GeminiSessionFileAdapter } from './core/session/adapters/gemini-session-file-adapter.js';
|
|
15
|
+
import { EcagentSessionFileAdapter } from './core/session/adapters/ecagent-session-file-adapter.js';
|
|
15
16
|
import { loadDefaults, loadAllAgents, migrateIdentitiesIfNeeded, loadDaemonConfig, initializeEckSnapshotsConfig } from './config-store.js';
|
|
16
17
|
import { ConfigTarget, initConfigManager, onConfigWrite } from './config/config-manager.js';
|
|
17
18
|
import { ensureRoleConfigV4OnStartup } from './config/role-config-v4-startup.js';
|
|
@@ -30,6 +31,7 @@ import { SessionManager } from './core/session/session-manager.js';
|
|
|
30
31
|
import { ClaudeAgentPlugin } from './agents/claude-runner.js';
|
|
31
32
|
import { CodexAgentPlugin } from './agents/codex-runner.js';
|
|
32
33
|
import { GeminiAgentPlugin } from './agents/gemini-runner.js';
|
|
34
|
+
import { EcagentAgentPlugin } from './agents/ecagent-runner.js';
|
|
33
35
|
import { FeishuChannelPlugin, registerPendingFeishuContactBind } from './channels/feishu.js';
|
|
34
36
|
import { WechatChannelPlugin, registerPendingWechatContactBind } from './channels/wechat.js';
|
|
35
37
|
import { AUN_HANDOFF_MARKER_FIELD, AUNChannel, AUNChannelPlugin } from './channels/aun.js';
|
|
@@ -78,13 +80,13 @@ import { TriggerAuditLogger } from './trigger/audit.js';
|
|
|
78
80
|
import { TriggerScriptExecutor } from './trigger/script-executor.js';
|
|
79
81
|
import { TriggerFeedbackDispatcher } from './trigger/feedback.js';
|
|
80
82
|
import { TriggerRuntimeScheduler } from './trigger/scheduler.js';
|
|
83
|
+
import { TargetSessionLockRegistry } from './trigger/session-lock.js';
|
|
81
84
|
import { DaemonChannel } from './channels/daemon.js';
|
|
82
85
|
import { definitionRevision, normalizeTriggerDefinition } from './trigger/validation.js';
|
|
83
86
|
import { applyTriggerPatch } from './trigger/patch.js';
|
|
84
87
|
import { validateModelSelectionForRole } from './core/model/model-permission.js';
|
|
85
88
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride, } from './core/role/runtime-policy.js';
|
|
86
89
|
import { atomicWriteJson } from './core/session/session-fs-store.js';
|
|
87
|
-
import { cleanupLegacySystemControlSessions } from './core/system-channels.js';
|
|
88
90
|
import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from './core/message/message-log.js';
|
|
89
91
|
import fs from 'fs';
|
|
90
92
|
import crypto from 'crypto';
|
|
@@ -844,21 +846,19 @@ async function main() {
|
|
|
844
846
|
});
|
|
845
847
|
return roleToSessionIdentity(detail.effectiveRole);
|
|
846
848
|
};
|
|
847
|
-
const removedLegacyControlSessions = cleanupLegacySystemControlSessions(paths.sessionsDir);
|
|
848
|
-
if (removedLegacyControlSessions.length > 0) {
|
|
849
|
-
logger.info(`[startup] Removed legacy synthetic control sessions: ${removedLegacyControlSessions.join(', ')}`);
|
|
850
|
-
}
|
|
851
849
|
const sessionManager = new SessionManager(paths.sessionsDir, eventBus, resolveSessionIdentity);
|
|
852
850
|
logger.info('✓ Database initialized');
|
|
853
851
|
// 注册会话文件适配器(Claude / Codex 各自的会话文件操作)
|
|
854
852
|
sessionManager.registerFileAdapter(new ClaudeSessionFileAdapter());
|
|
855
853
|
sessionManager.registerFileAdapter(new CodexSessionFileAdapter());
|
|
856
854
|
sessionManager.registerFileAdapter(new GeminiSessionFileAdapter());
|
|
855
|
+
sessionManager.registerFileAdapter(new EcagentSessionFileAdapter());
|
|
857
856
|
// Agent 插件系统:每个 EvolAgent × 每个 baseagent 一个独立 runner(H1/H2 修复)
|
|
858
857
|
const agentLoader = new AgentLoader();
|
|
859
858
|
agentLoader.register(new ClaudeAgentPlugin());
|
|
860
859
|
agentLoader.register(new CodexAgentPlugin());
|
|
861
860
|
agentLoader.register(new GeminiAgentPlugin());
|
|
861
|
+
agentLoader.register(new EcagentAgentPlugin());
|
|
862
862
|
const agentInstances = agentLoader.createAll(agentRegistry, {
|
|
863
863
|
onSessionIdUpdate: async (sessionId, agentSessionId) => {
|
|
864
864
|
await sessionManager.updateAgentSessionIdBySessionId(sessionId, agentSessionId);
|
|
@@ -1010,7 +1010,7 @@ async function main() {
|
|
|
1010
1010
|
const messageQueue = new MessageQueue(async (message) => {
|
|
1011
1011
|
await processor.processMessage(message);
|
|
1012
1012
|
}, {
|
|
1013
|
-
persistencePath:
|
|
1013
|
+
persistencePath: daemonQueuePath(),
|
|
1014
1014
|
});
|
|
1015
1015
|
// 设置中断回调(精确中断正在处理的 agent)
|
|
1016
1016
|
messageQueue.setInterruptCallback(async (sessionKey, baseagent, evolagentName, reason = 'new_message') => {
|
|
@@ -1074,6 +1074,7 @@ async function main() {
|
|
|
1074
1074
|
responseEngine.setHandoffRuntime(handoffRuntime);
|
|
1075
1075
|
// Trigger runtime: daemon-level script + feedback scheduler.
|
|
1076
1076
|
const triggerSchedulers = new Map();
|
|
1077
|
+
const targetSessionLocks = new TargetSessionLockRegistry();
|
|
1077
1078
|
const triggerAudit = new TriggerAuditLogger();
|
|
1078
1079
|
const triggerScriptExecutor = new TriggerScriptExecutor();
|
|
1079
1080
|
const daemonChannel = new DaemonChannel(sessionManager, messageQueue, {
|
|
@@ -1446,7 +1447,7 @@ async function main() {
|
|
|
1446
1447
|
baseagent: owner.baseagent,
|
|
1447
1448
|
getBaseagent: () => agentRegistry.get(owner.aid)?.baseagent || owner.baseagent,
|
|
1448
1449
|
authorizeExecution: authorizeTriggerExecution,
|
|
1449
|
-
}, eventBus);
|
|
1450
|
+
}, eventBus, targetSessionLocks);
|
|
1450
1451
|
triggerSchedulers.set(owner.aid, scheduler);
|
|
1451
1452
|
try {
|
|
1452
1453
|
await scheduler.init();
|
|
@@ -1710,7 +1711,7 @@ async function main() {
|
|
|
1710
1711
|
}
|
|
1711
1712
|
const connectedChannels = new Set();
|
|
1712
1713
|
const onlineNoticeSent = new Set();
|
|
1713
|
-
const pendingFile = path.join(
|
|
1714
|
+
const pendingFile = path.join(daemonControlDir(), 'restart-pending.json');
|
|
1714
1715
|
let pendingRestartNoticeInFlight = null;
|
|
1715
1716
|
let pendingRestartNoticeSent = false;
|
|
1716
1717
|
const sendOnlineNoticeForChannel = (inst) => {
|
|
@@ -1960,7 +1961,9 @@ async function main() {
|
|
|
1960
1961
|
}
|
|
1961
1962
|
}
|
|
1962
1963
|
else {
|
|
1963
|
-
|
|
1964
|
+
// 正式安装时 ECWeb 是独立的全局 ec-web 包,由 ec start/restart 的
|
|
1965
|
+
// 启动链路管理;runtime 目录没有内嵌构建产物是正常状态。
|
|
1966
|
+
logger.debug(`ECWeb runtime entry absent (managed externally): ${ecwebEntry}`);
|
|
1964
1967
|
}
|
|
1965
1968
|
}
|
|
1966
1969
|
// 控制 AID 接收 owner 指令:
|