evolcore 0.0.32 → 0.0.34
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 +61 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +135 -37
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +111 -15
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +112 -54
- package/dist/agents/codex-runner.js +241 -63
- package/dist/agents/ecagent-runner.js +78 -33
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +44 -8
- package/dist/channels/daemon.js +261 -3
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +368 -31
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +197 -27
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +98 -67
- package/dist/cli/init.js +2 -2
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +166 -146
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +622 -0
- package/dist/cli/trigger-command.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +6 -3
- package/dist/config/config-manager.js +26 -2
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +45 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +133 -36
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +25 -2
- package/dist/core/auth/operation-catalog.js +49 -0
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +197 -27
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +51 -1
- package/dist/core/command/menu-handler.js +114 -23
- package/dist/core/command/menu-protocol.js +25 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +195 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +175 -3
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +83 -29
- package/dist/core/memory/consolidation-ops-records.js +49 -16
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +264 -205
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +30 -15
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +169 -10
- package/dist/core/message/message-bridge.js +15 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +929 -110
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +217 -9
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +129 -14
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +21 -4
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +770 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +19 -7
- package/dist/ipc.js +140 -50
- package/dist/paths.js +93 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +13 -2
- package/dist/trigger/parser.js +70 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +143 -1
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +40 -28
- package/kits/docs/evolcore/agent.md +21 -6
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +5 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +4 -6
- package/kits/docs/evolcore/group.md +11 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -15
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +8 -11
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -1
- package/kits/eck_manifest.json +55 -40
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +199 -0
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +16 -2
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +7 -2
- package/kits/templates/memory-consolidation/generalize.md +9 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +8 -3
- package/kits/templates/memory-consolidation/promote.md +8 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +67 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +7 -6
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +6 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -8,6 +8,7 @@ import { validateModelSelectionForRole } from '../core/model/model-permission.js
|
|
|
8
8
|
import { isValidAunDeliveryForChannel } from '../core/message/outbound.js';
|
|
9
9
|
import { tryParseChannelKey } from '../core/channel-loader.js';
|
|
10
10
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride, } from '../core/role/runtime-policy.js';
|
|
11
|
+
import { resolveThreadRouteTarget } from '../core/thread/thread-route.js';
|
|
11
12
|
export function originFromActorSession(session, authenticatedPeerId, authenticatedChannelKey, authenticatedChatType) {
|
|
12
13
|
const peerId = authenticatedPeerId || session.metadata?.peerId;
|
|
13
14
|
if (!peerId)
|
|
@@ -15,16 +16,21 @@ export function originFromActorSession(session, authenticatedPeerId, authenticat
|
|
|
15
16
|
const sessionChannelKey = session.metadata?.channelKey || session.channel;
|
|
16
17
|
const channelKey = authenticatedChannelKey || sessionChannelKey;
|
|
17
18
|
const sameConversation = channelKey === sessionChannelKey;
|
|
19
|
+
if (authenticatedChatType && (authenticatedChatType === 'group') !== (session.chatType === 'group')) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
18
22
|
const isGroup = authenticatedChatType === 'group' || session.chatType === 'group';
|
|
23
|
+
const channelId = isGroup ? session.channelId : peerId;
|
|
24
|
+
const sessionKind = sameConversation && session.threadId ? 'thread' : 'main';
|
|
19
25
|
return {
|
|
20
26
|
channelKey,
|
|
21
27
|
channelType: session.channelType || tryParseChannelKey(channelKey)?.type || channelKey,
|
|
22
|
-
channelId
|
|
23
|
-
session:
|
|
24
|
-
threadId:
|
|
28
|
+
channelId,
|
|
29
|
+
session: sessionKind,
|
|
30
|
+
threadId: sessionKind === 'thread' ? session.threadId : undefined,
|
|
25
31
|
peerId,
|
|
26
|
-
sessionKey: sameConversation
|
|
27
|
-
delivery: { chatType: 'private' },
|
|
32
|
+
sessionKey: sameConversation ? session.sessionKey : undefined,
|
|
33
|
+
delivery: isGroup ? { chatType: 'group', groupId: channelId } : { chatType: 'private' },
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
export function daemonPrivilegedTriggerReason(definition) {
|
|
@@ -76,6 +82,36 @@ export function validateTriggerFeedbackChannels(definition, getTriggerChannel) {
|
|
|
76
82
|
export function hasDaemonTriggerAuthority(subject) {
|
|
77
83
|
return subject.isDaemonOwner || subject.processRole === 'daemon-service' || subject.dataScope === 'daemon';
|
|
78
84
|
}
|
|
85
|
+
function triggerOriginChatType(origin) {
|
|
86
|
+
return origin.delivery?.chatType ?? 'private';
|
|
87
|
+
}
|
|
88
|
+
function feedbackTargetMatchesOrigin(target, origin) {
|
|
89
|
+
return target.channelKey === origin.channelKey
|
|
90
|
+
&& target.channelId === origin.channelId
|
|
91
|
+
&& target.session === origin.session
|
|
92
|
+
&& (target.threadId ?? '') === (origin.threadId ?? '')
|
|
93
|
+
&& (target.chatType === undefined || target.chatType === triggerOriginChatType(origin));
|
|
94
|
+
}
|
|
95
|
+
function feedbackTargetIsSamePeerThread(definition, target, origin) {
|
|
96
|
+
if (!target || !origin || target.session !== 'thread' || !target.threadId)
|
|
97
|
+
return false;
|
|
98
|
+
if (target.channelKey !== origin.channelKey || target.channelId !== origin.channelId)
|
|
99
|
+
return false;
|
|
100
|
+
const originChatType = triggerOriginChatType(origin);
|
|
101
|
+
if (target.chatType !== originChatType)
|
|
102
|
+
return false;
|
|
103
|
+
return !!resolveThreadRouteTarget(definition.agentAid, target.threadId, target.chatType === 'group' ? 'group' : 'msg', target.channelId);
|
|
104
|
+
}
|
|
105
|
+
function feedbackTargetAllowedForNonManagement(definition, origin) {
|
|
106
|
+
const target = definition.feedback.target;
|
|
107
|
+
if (!target || !origin)
|
|
108
|
+
return false;
|
|
109
|
+
if (feedbackTargetMatchesOrigin(target, origin))
|
|
110
|
+
return true;
|
|
111
|
+
if (target.chatType === 'group')
|
|
112
|
+
return false;
|
|
113
|
+
return feedbackTargetIsSamePeerThread(definition, target, origin);
|
|
114
|
+
}
|
|
79
115
|
export function validateTriggerDefinitionForActor(definition, actor, dependencies) {
|
|
80
116
|
if (definition.execution.permissionMode === 'fullaccess') {
|
|
81
117
|
if (!actor.daemonOwner && !actor.fullAccess) {
|
|
@@ -127,14 +163,8 @@ export function validateTriggerDefinitionForActor(definition, actor, dependencie
|
|
|
127
163
|
throw new Error('event-session feedback requires owner/admin');
|
|
128
164
|
}
|
|
129
165
|
if (!actor.management && definition.feedback.strategy === 'target') {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!target || !origin
|
|
133
|
-
|| target.channelKey !== origin.channelKey
|
|
134
|
-
|| target.channelId !== origin.channelId
|
|
135
|
-
|| target.session !== origin.session
|
|
136
|
-
|| (target.threadId ?? '') !== (origin.threadId ?? '')) {
|
|
137
|
-
throw new Error('non-management trigger target must be the creator private conversation');
|
|
166
|
+
if (!feedbackTargetAllowedForNonManagement(definition, actor.origin)) {
|
|
167
|
+
throw new Error('non-management trigger target must be the creator conversation or an existing same-peer private thread');
|
|
138
168
|
}
|
|
139
169
|
}
|
|
140
170
|
}
|
|
@@ -187,13 +217,15 @@ export function authorizeTriggerExecution(definition, dependencies) {
|
|
|
187
217
|
const channelType = origin.channelType || tryParseChannelKey(origin.channelKey)?.type;
|
|
188
218
|
if (!channelType)
|
|
189
219
|
return { allowed: false, reason: 'trigger origin channel type is missing' };
|
|
220
|
+
const chatType = triggerOriginChatType(origin);
|
|
221
|
+
const conversationId = chatType === 'group' ? origin.channelId : origin.peerId;
|
|
190
222
|
const roleDetail = resolvePeerRoleDetail({
|
|
191
223
|
selfAid: definition.agentAid,
|
|
192
224
|
channelKey: origin.channelKey,
|
|
193
225
|
channelType,
|
|
194
|
-
chatType
|
|
226
|
+
chatType,
|
|
195
227
|
actorId: origin.peerId,
|
|
196
|
-
conversationId
|
|
228
|
+
conversationId,
|
|
197
229
|
});
|
|
198
230
|
const role = roleDetail.effectiveRole;
|
|
199
231
|
if (!checkRoleAccess(role, definition.agentAid)) {
|
|
@@ -205,8 +237,8 @@ export function authorizeTriggerExecution(definition, dependencies) {
|
|
|
205
237
|
channel: origin.channelKey,
|
|
206
238
|
channelType,
|
|
207
239
|
channelId: origin.channelId,
|
|
208
|
-
chatType
|
|
209
|
-
conversationId
|
|
240
|
+
chatType,
|
|
241
|
+
conversationId,
|
|
210
242
|
roleDetail,
|
|
211
243
|
identity: roleToSessionIdentity(role),
|
|
212
244
|
processOwners: dependencies.getDaemonOwners(),
|
|
@@ -236,13 +268,8 @@ export function authorizeTriggerExecution(definition, dependencies) {
|
|
|
236
268
|
return { allowed: false, reason: 'event-session feedback requires current owner/admin role' };
|
|
237
269
|
}
|
|
238
270
|
if (definition.feedback.strategy === 'target' && !isManagementRole(role)) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|| target.channelKey !== origin.channelKey
|
|
242
|
-
|| target.channelId !== origin.channelId
|
|
243
|
-
|| target.session !== origin.session
|
|
244
|
-
|| (target.threadId ?? '') !== (origin.threadId ?? '')) {
|
|
245
|
-
return { allowed: false, reason: `cross-conversation target requires current owner/admin role (role=${role ?? 'none'})` };
|
|
271
|
+
if (!feedbackTargetAllowedForNonManagement(definition, origin)) {
|
|
272
|
+
return { allowed: false, reason: `cross-conversation target requires current owner/admin role or an existing same-peer private thread (role=${role ?? 'none'})` };
|
|
246
273
|
}
|
|
247
274
|
}
|
|
248
275
|
const permissionDecision = constrainRuntimePermissionMode({
|
|
@@ -314,13 +341,15 @@ export async function authenticatedTriggerActor(dependencies, agentAid, actorSes
|
|
|
314
341
|
if (validation.grant.actorId !== actorOrigin.peerId) {
|
|
315
342
|
throw new Error('trigger delegation actor does not match session peer');
|
|
316
343
|
}
|
|
344
|
+
const actorChatType = triggerOriginChatType(actorOrigin);
|
|
345
|
+
const actorConversationId = actorChatType === 'group' ? actorOrigin.channelId : actorOrigin.peerId;
|
|
317
346
|
const roleDetail = resolvePeerRoleDetail({
|
|
318
347
|
selfAid: agentAid,
|
|
319
348
|
channelKey: validation.grant.channel,
|
|
320
349
|
channelType: validation.grant.channelType,
|
|
321
|
-
chatType:
|
|
350
|
+
chatType: actorChatType,
|
|
322
351
|
actorId: validation.grant.actorId,
|
|
323
|
-
conversationId:
|
|
352
|
+
conversationId: actorConversationId,
|
|
324
353
|
});
|
|
325
354
|
const currentRole = roleDetail.effectiveRole;
|
|
326
355
|
const subject = buildAuthSubject({
|
|
@@ -329,8 +358,8 @@ export async function authenticatedTriggerActor(dependencies, agentAid, actorSes
|
|
|
329
358
|
channel: validation.grant.channel,
|
|
330
359
|
channelType: validation.grant.channelType,
|
|
331
360
|
channelId: actorOrigin.channelId,
|
|
332
|
-
chatType:
|
|
333
|
-
conversationId:
|
|
361
|
+
chatType: actorChatType,
|
|
362
|
+
conversationId: actorConversationId,
|
|
334
363
|
roleDetail,
|
|
335
364
|
identity: roleToSessionIdentity(currentRole),
|
|
336
365
|
processOwners: dependencies.getDaemonOwners(),
|
package/dist/trigger/feedback.js
CHANGED
|
@@ -272,10 +272,16 @@ export class TriggerFeedbackDispatcher {
|
|
|
272
272
|
originChannelKey: eventSession ? target.channelKey : input.trigger.origin?.channelKey,
|
|
273
273
|
originChannelType: eventSession ? channelType : input.trigger.origin?.channelType
|
|
274
274
|
?? (input.trigger.origin?.channelKey ? channelTypeFromKey(input.trigger.origin.channelKey) : undefined),
|
|
275
|
-
|
|
275
|
+
originChatType: eventSession ? messageChatType : input.trigger.origin?.delivery?.chatType,
|
|
276
|
+
originConversationId: eventSession
|
|
277
|
+
? target.channelId
|
|
278
|
+
: input.trigger.origin?.delivery?.chatType === 'group'
|
|
279
|
+
? input.trigger.origin.channelId
|
|
280
|
+
: input.trigger.origin?.peerId,
|
|
276
281
|
originControl: !eventSession && input.trigger.origin?.channelKey === 'daemon',
|
|
277
282
|
noopSentinel: input.trigger.execution.noopSentinel,
|
|
278
283
|
authorizedBy: input.trigger.authorizedBy,
|
|
284
|
+
labels: input.trigger.labels,
|
|
279
285
|
},
|
|
280
286
|
};
|
|
281
287
|
outcome = this.waitForTriggerOutcome(input.runId, input.attemptId, delivery.signal);
|
|
@@ -297,7 +303,12 @@ export class TriggerFeedbackDispatcher {
|
|
|
297
303
|
});
|
|
298
304
|
await binding.adapter.send(envelope, { kind: 'status.error',
|
|
299
305
|
metadata: { errorType: 'orphaned_target_session', message: text } });
|
|
300
|
-
await binding.adapter.send(envelope, {
|
|
306
|
+
await binding.adapter.send(envelope, {
|
|
307
|
+
kind: 'result.error',
|
|
308
|
+
subtype: 'session_unavailable',
|
|
309
|
+
reason: 'orphaned_target_session',
|
|
310
|
+
text,
|
|
311
|
+
});
|
|
301
312
|
};
|
|
302
313
|
const inspect = async () => {
|
|
303
314
|
if (!this.deps.inspectTargetExecution || disposed || delivery.signal.aborted)
|
package/dist/trigger/parser.js
CHANGED
|
@@ -11,7 +11,7 @@ const TRIGGER_UPDATE_FLAGS = new Set([
|
|
|
11
11
|
]);
|
|
12
12
|
const TRIGGER_CREATE_FLAGS = new Set([
|
|
13
13
|
'once', 'delay', 'at', 'cron', 'every', 'event', 'event-filter', 'tz',
|
|
14
|
-
'exec', 'prompt', 'name', 'agent',
|
|
14
|
+
'exec', 'prompt', 'name', 'description', 'agent', 'label',
|
|
15
15
|
'script-path', 'script-runtime', 'script-args', 'script-timeout', 'execution-timeout',
|
|
16
16
|
'feedback', 'target-channel', 'target-channel-id', 'target-chat-type', 'target-session', 'target-thread-id',
|
|
17
17
|
'trigger-thread', 'baseagent',
|
|
@@ -68,6 +68,60 @@ export function parseDuration(s) {
|
|
|
68
68
|
const multiplier = unit === 'd' ? 86_400_000 : unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : 1_000;
|
|
69
69
|
return amount * multiplier;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* T10: 从原始 args 里提取 `--label key=value`(支持多次出现,值内可用逗号分隔多对)。
|
|
73
|
+
* 约定:labels 若存在则必须含 `kind`(渲染模板路由 key)。
|
|
74
|
+
*/
|
|
75
|
+
export function extractLabels(args) {
|
|
76
|
+
const tokens = [];
|
|
77
|
+
if (Array.isArray(args)) {
|
|
78
|
+
for (let i = 0; i < args.length; i++) {
|
|
79
|
+
const a = args[i];
|
|
80
|
+
if (a === '--label') {
|
|
81
|
+
const next = args[i + 1];
|
|
82
|
+
if (next === undefined || next.startsWith('--'))
|
|
83
|
+
return { error: '--label 不能为空(格式:--label key=value)' };
|
|
84
|
+
tokens.push(next);
|
|
85
|
+
i += 1;
|
|
86
|
+
}
|
|
87
|
+
else if (a.startsWith('--label=')) {
|
|
88
|
+
tokens.push(a.slice('--label='.length));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const re = /--label(?:=(\S+)|(?:\s+(?!--)(\S+)))?/g;
|
|
94
|
+
let m;
|
|
95
|
+
while ((m = re.exec(args)) !== null) {
|
|
96
|
+
const v = m[1] ?? m[2];
|
|
97
|
+
if (v === undefined)
|
|
98
|
+
return { error: '--label 不能为空(格式:--label key=value)' };
|
|
99
|
+
tokens.push(v.replace(/^["']|["']$/g, ''));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (tokens.length === 0)
|
|
103
|
+
return { labels: {} };
|
|
104
|
+
const labels = {};
|
|
105
|
+
for (const token of tokens) {
|
|
106
|
+
for (const pair of token.split(',')) {
|
|
107
|
+
const trimmed = pair.trim();
|
|
108
|
+
if (!trimmed)
|
|
109
|
+
continue;
|
|
110
|
+
const eq = trimmed.indexOf('=');
|
|
111
|
+
if (eq <= 0)
|
|
112
|
+
return { error: `--label 格式无效: "${trimmed}"(应为 key=value)` };
|
|
113
|
+
const key = trimmed.slice(0, eq).trim();
|
|
114
|
+
const value = trimmed.slice(eq + 1);
|
|
115
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
116
|
+
return { error: `--label key 名无效: "${key}"(只允许字母、数字、下划线,且不以数字开头)` };
|
|
117
|
+
}
|
|
118
|
+
labels[key] = value;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!('kind' in labels))
|
|
122
|
+
return { error: '--label 必须包含 kind(如 --label kind=default)' };
|
|
123
|
+
return { labels };
|
|
124
|
+
}
|
|
71
125
|
export function parseIsoDate(s) {
|
|
72
126
|
const d = new Date(s);
|
|
73
127
|
return isNaN(d.getTime()) ? null : d.getTime();
|
|
@@ -390,6 +444,9 @@ function commonParsed(flags, opts = {}) {
|
|
|
390
444
|
return { ok: false, error: '--script-args 必须是合法的 JSON' };
|
|
391
445
|
}
|
|
392
446
|
}
|
|
447
|
+
const description = optionalFlagString(flags, 'description');
|
|
448
|
+
if ('error' in description)
|
|
449
|
+
return { ok: false, error: description.error };
|
|
393
450
|
const prompt = flags.get('prompt');
|
|
394
451
|
if (prompt === true)
|
|
395
452
|
return { ok: false, error: '--prompt 不能为空;复杂内容建议改用 --prompt-file <path>' };
|
|
@@ -471,6 +528,8 @@ function commonParsed(flags, opts = {}) {
|
|
|
471
528
|
targetThreadId: targetThreadId,
|
|
472
529
|
agentId: flags.has('agent') ? flags.get('agent') : undefined,
|
|
473
530
|
name: flags.has('name') ? flags.get('name') : undefined,
|
|
531
|
+
// create 语义:空字符串等同于「未提供」,不写该字段(与 update 的清除语义不同)。
|
|
532
|
+
description: description.value || undefined,
|
|
474
533
|
prompt: prompt,
|
|
475
534
|
scriptPath: scriptPath,
|
|
476
535
|
scriptRuntime: scriptRuntime,
|
|
@@ -496,6 +555,9 @@ export function parseTriggerSet(args) {
|
|
|
496
555
|
if (!parsed.ok)
|
|
497
556
|
return { ok: false, error: withPromptFileHint(parsed.error, args) };
|
|
498
557
|
const value = parsed.value;
|
|
558
|
+
const labelsResult = extractLabels(args);
|
|
559
|
+
if ('error' in labelsResult)
|
|
560
|
+
return { ok: false, error: labelsResult.error };
|
|
499
561
|
return {
|
|
500
562
|
ok: true,
|
|
501
563
|
value: {
|
|
@@ -512,6 +574,7 @@ export function parseTriggerSet(args) {
|
|
|
512
574
|
targetThreadId: value.targetThreadId,
|
|
513
575
|
agentId: value.agentId,
|
|
514
576
|
name: value.name,
|
|
577
|
+
description: value.description,
|
|
515
578
|
prompt: value.prompt,
|
|
516
579
|
scriptPath: value.scriptPath,
|
|
517
580
|
scriptRuntime: value.scriptRuntime,
|
|
@@ -525,6 +588,7 @@ export function parseTriggerSet(args) {
|
|
|
525
588
|
model: value.model,
|
|
526
589
|
effort: value.effort,
|
|
527
590
|
permissionMode: value.permissionMode,
|
|
591
|
+
...(Object.keys(labelsResult.labels).length > 0 ? { labels: labelsResult.labels } : {}),
|
|
528
592
|
},
|
|
529
593
|
};
|
|
530
594
|
}
|
|
@@ -538,6 +602,9 @@ export function parseTriggerSetArgv(args, opts = {}) {
|
|
|
538
602
|
if (!parsed.ok)
|
|
539
603
|
return { ok: false, error: withPromptFileHint(parsed.error, args, opts) };
|
|
540
604
|
const value = parsed.value;
|
|
605
|
+
const labelsResult = extractLabels(args);
|
|
606
|
+
if ('error' in labelsResult)
|
|
607
|
+
return { ok: false, error: labelsResult.error };
|
|
541
608
|
return {
|
|
542
609
|
ok: true,
|
|
543
610
|
value: {
|
|
@@ -554,6 +621,7 @@ export function parseTriggerSetArgv(args, opts = {}) {
|
|
|
554
621
|
targetThreadId: value.targetThreadId,
|
|
555
622
|
agentId: value.agentId,
|
|
556
623
|
name: value.name,
|
|
624
|
+
description: value.description,
|
|
557
625
|
prompt: value.prompt,
|
|
558
626
|
scriptPath: value.scriptPath,
|
|
559
627
|
scriptRuntime: value.scriptRuntime,
|
|
@@ -567,6 +635,7 @@ export function parseTriggerSetArgv(args, opts = {}) {
|
|
|
567
635
|
model: value.model,
|
|
568
636
|
effort: value.effort,
|
|
569
637
|
permissionMode: value.permissionMode,
|
|
638
|
+
...(Object.keys(labelsResult.labels).length > 0 ? { labels: labelsResult.labels } : {}),
|
|
570
639
|
},
|
|
571
640
|
};
|
|
572
641
|
}
|
|
@@ -121,7 +121,7 @@ export function seedTaskRecoveryTrigger(manager, owner) {
|
|
|
121
121
|
fs.rmSync(manager.activePath(definition.id), { force: true });
|
|
122
122
|
}
|
|
123
123
|
const SESSION_IDLE_CHECK_PROMPT = `本会话已闲置一段时间。请依据你的职责执行一次例行巡检:
|
|
124
|
-
1.
|
|
124
|
+
1. 检查当前会话中未完成却停滞的事项。私聊检查自己承诺或正在推进的任务;群聊还需检查成员长时间无进展、被点名未回应、承诺后失联等情况。有,则在既有授权和职责内推进一次,或向相关成员催促一次并说明停滞事项,随后即止;无,则不做任何动作。不要自行新增任务,正常推进或无待办时保持沉默。
|
|
125
125
|
2. 巡检优先基于已有上下文判断,确需补充背景再查询历史,并遵守会话自身的发送规范。
|
|
126
126
|
3. 判定无需任何对外动作时,本轮只输出 [[NOOP]],不发送任何消息、不发布巡检结论。注意:最终对外输出必须是且仅是 [[NOOP]] 这一行——不要在输出它之前先发送任何说明文字或巡检判定,判定过程仅在内部完成。
|
|
127
127
|
(本指令为通用模板,可按具体职责增删检查项。)`;
|
|
@@ -133,7 +133,7 @@ export function seedSessionIdleCheckTrigger(manager, owner) {
|
|
|
133
133
|
agentAid: owner.aid,
|
|
134
134
|
enabled: true,
|
|
135
135
|
name: '__session-idle-check',
|
|
136
|
-
description: 'System trigger: inspect configured idle group sessions.',
|
|
136
|
+
description: 'System trigger: inspect configured idle private and group sessions.',
|
|
137
137
|
createdAt: now,
|
|
138
138
|
updatedAt: now,
|
|
139
139
|
origin: {
|
|
@@ -147,7 +147,7 @@ export function seedSessionIdleCheckTrigger(manager, owner) {
|
|
|
147
147
|
source: {
|
|
148
148
|
type: 'event',
|
|
149
149
|
eventPattern: 'session:idle',
|
|
150
|
-
filter: { match: { chatType: 'group' } },
|
|
150
|
+
filter: { match: { chatType: { $in: ['private', 'group'] } } },
|
|
151
151
|
},
|
|
152
152
|
execution: {
|
|
153
153
|
type: 'target_session',
|
|
@@ -252,6 +252,20 @@ function normalizeV3Phase2(raw, opts) {
|
|
|
252
252
|
reliability: normalizeReliability(raw.reliability),
|
|
253
253
|
limits: normalizeLimits(raw.limits),
|
|
254
254
|
};
|
|
255
|
+
// T10: labels 字典原样保留,只做类型校验(如果存在必须是 Record<string, string>)
|
|
256
|
+
const rawLabels = raw.labels;
|
|
257
|
+
if (rawLabels !== undefined && rawLabels !== null) {
|
|
258
|
+
if (typeof rawLabels !== 'object' || Array.isArray(rawLabels)) {
|
|
259
|
+
throw new Error('labels must be a plain object');
|
|
260
|
+
}
|
|
261
|
+
const labels = {};
|
|
262
|
+
for (const [k, v] of Object.entries(rawLabels)) {
|
|
263
|
+
if (typeof v !== 'string')
|
|
264
|
+
throw new Error(`labels.${k} must be a string`);
|
|
265
|
+
labels[k] = v;
|
|
266
|
+
}
|
|
267
|
+
definition.labels = labels;
|
|
268
|
+
}
|
|
255
269
|
validateTriggerSemantics(definition);
|
|
256
270
|
return definition;
|
|
257
271
|
}
|
|
@@ -666,6 +666,13 @@ export async function waitForProcessExit(pid, timeoutMs = 10_000, intervalMs = 2
|
|
|
666
666
|
* Returns list of matching PIDs.
|
|
667
667
|
*/
|
|
668
668
|
export function findProcesses(pattern) {
|
|
669
|
+
return findProcessesWithCommandLine(pattern).map(match => match.pid);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Like findProcesses, but keeps the command line returned by the Windows CIM
|
|
673
|
+
* snapshot so callers do not need a second PowerShell launch per candidate.
|
|
674
|
+
*/
|
|
675
|
+
export function findProcessesWithCommandLine(pattern) {
|
|
669
676
|
try {
|
|
670
677
|
if (isWindows) {
|
|
671
678
|
// WMI LIKE is wildcard matching, not JavaScript regex matching. Query a
|
|
@@ -696,13 +703,15 @@ export function findProcesses(pattern) {
|
|
|
696
703
|
const normalized = commandLine.replace(/\\/g, '/');
|
|
697
704
|
return matcherCandidates.some(matcher => matcher.test(commandLine) || matcher.test(normalized));
|
|
698
705
|
})
|
|
699
|
-
.map(row => row.ProcessId);
|
|
706
|
+
.map(row => ({ pid: row.ProcessId, commandLine: row.CommandLine || '' }));
|
|
700
707
|
}
|
|
701
708
|
return [];
|
|
702
709
|
}
|
|
703
710
|
else {
|
|
704
711
|
const output = execFileSync('pgrep', ['-f', pattern], { encoding: 'utf-8' }).trim();
|
|
705
|
-
return output
|
|
712
|
+
return output
|
|
713
|
+
? output.split('\n').map(Number).filter(pid => pid !== process.pid).map(pid => ({ pid }))
|
|
714
|
+
: [];
|
|
706
715
|
}
|
|
707
716
|
}
|
|
708
717
|
catch {
|
|
@@ -85,11 +85,25 @@
|
|
|
85
85
|
"message": "❌ 当前模型不受支持,请正确配置模型后重试"
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
"id": "
|
|
89
|
-
"match": "api error: 403",
|
|
90
|
-
"action": "
|
|
88
|
+
"id": "claude-client-upgrade-403-cn",
|
|
89
|
+
"match": "api error: 403 请升级claude code 客户端到最新版",
|
|
90
|
+
"action": "stop",
|
|
91
|
+
"type": "api_error",
|
|
92
|
+
"message": "⚠️ Claude Code 客户端版本过旧,请升级到最新版后重试"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "insufficient-credit-403-cn",
|
|
96
|
+
"match": "api error: 403 预扣后为-",
|
|
97
|
+
"action": "stop",
|
|
91
98
|
"type": "api_error",
|
|
92
|
-
"message": "⚠️
|
|
99
|
+
"message": "⚠️ 积分余额不足,请充值积分或升级套餐后重试"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "team-context-limit-403-cn",
|
|
103
|
+
"match": "api error: 403 请求上下文约",
|
|
104
|
+
"action": "stop",
|
|
105
|
+
"type": "context_too_long",
|
|
106
|
+
"message": "⚠️ 请求已超过团队上下文上限,请使用 /compact 压缩上下文后重试,或联系管理员调整上下文配额"
|
|
93
107
|
},
|
|
94
108
|
{
|
|
95
109
|
"id": "api-error-429",
|
|
@@ -91,6 +91,117 @@ export var ErrorType;
|
|
|
91
91
|
ErrorType["CLAUDE_CODE_STARTUP_FAILED"] = "claude_code_startup_failed";
|
|
92
92
|
ErrorType["UNKNOWN"] = "unknown";
|
|
93
93
|
})(ErrorType || (ErrorType = {}));
|
|
94
|
+
export function getErrorRawText(error, seen = new Set()) {
|
|
95
|
+
if (typeof error === 'string') {
|
|
96
|
+
const text = error.trim();
|
|
97
|
+
return text || undefined;
|
|
98
|
+
}
|
|
99
|
+
if (error === null || error === undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
if (typeof error === 'object') {
|
|
102
|
+
const record = error;
|
|
103
|
+
if (seen.has(error))
|
|
104
|
+
return undefined;
|
|
105
|
+
seen.add(error);
|
|
106
|
+
if ('raw' in record) {
|
|
107
|
+
const raw = record.raw;
|
|
108
|
+
if (typeof raw === 'string' && raw.trim())
|
|
109
|
+
return raw.trim();
|
|
110
|
+
}
|
|
111
|
+
const causeText = getErrorRawText(record.cause, seen);
|
|
112
|
+
if (causeText)
|
|
113
|
+
return causeText;
|
|
114
|
+
for (const key of ['message', 'error', 'reason']) {
|
|
115
|
+
const value = record[key];
|
|
116
|
+
if (typeof value === 'string' && value.trim())
|
|
117
|
+
return value.trim();
|
|
118
|
+
if (value && typeof value === 'object') {
|
|
119
|
+
const nestedText = getErrorRawText(value, seen);
|
|
120
|
+
if (nestedText)
|
|
121
|
+
return nestedText;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const code = record.code;
|
|
125
|
+
if (typeof code === 'string' && code.trim())
|
|
126
|
+
return code.trim();
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const text = String(error).trim();
|
|
130
|
+
return text || undefined;
|
|
131
|
+
}
|
|
132
|
+
export function normalizeCoreErrorSubtype(error, reportedSubtype, terminalReason) {
|
|
133
|
+
const reported = reportedSubtype?.trim().toLowerCase();
|
|
134
|
+
if (error instanceof OrphanedTargetSessionError)
|
|
135
|
+
return ErrorType.SESSION_UNAVAILABLE;
|
|
136
|
+
if (error instanceof ExecutionTimeoutError)
|
|
137
|
+
return ErrorType.SDK_TIMEOUT;
|
|
138
|
+
if (terminalReason === 'prompt_too_long' || terminalReason === 'context_compact_failed') {
|
|
139
|
+
return ErrorType.CONTEXT_TOO_LONG;
|
|
140
|
+
}
|
|
141
|
+
if (terminalReason === 'max_output_tokens')
|
|
142
|
+
return ErrorType.MAX_OUTPUT_TOKENS;
|
|
143
|
+
if (terminalReason === 'model_error')
|
|
144
|
+
return ErrorType.MODEL_UNAVAILABLE;
|
|
145
|
+
if (terminalReason === 'permission_denied')
|
|
146
|
+
return 'permission_error';
|
|
147
|
+
if (terminalReason === 'timeout' || terminalReason === 'total_execution_timeout')
|
|
148
|
+
return ErrorType.SDK_TIMEOUT;
|
|
149
|
+
if (reported === 'protocol_incomplete')
|
|
150
|
+
return 'protocol_incomplete';
|
|
151
|
+
if (reported === 'permission_denied' || reported?.startsWith('permission_'))
|
|
152
|
+
return 'permission_error';
|
|
153
|
+
const classified = classifyError(typeof error === 'string' ? new Error(error) : error);
|
|
154
|
+
if (classified !== ErrorType.UNKNOWN)
|
|
155
|
+
return classified;
|
|
156
|
+
if (reported && Object.values(ErrorType).includes(reported))
|
|
157
|
+
return reported;
|
|
158
|
+
switch (reported) {
|
|
159
|
+
case 'error_api':
|
|
160
|
+
case 'api':
|
|
161
|
+
case 'infrastructure':
|
|
162
|
+
return ErrorType.API_ERROR;
|
|
163
|
+
case 'error_model':
|
|
164
|
+
case 'model_error':
|
|
165
|
+
return ErrorType.MODEL_UNAVAILABLE;
|
|
166
|
+
case 'error_tool_use':
|
|
167
|
+
case 'tool_error':
|
|
168
|
+
return ErrorType.STREAM_ERROR;
|
|
169
|
+
}
|
|
170
|
+
if (terminalReason === 'rapid_refill_breaker')
|
|
171
|
+
return ErrorType.API_ERROR;
|
|
172
|
+
if (terminalReason === 'tool_error')
|
|
173
|
+
return ErrorType.STREAM_ERROR;
|
|
174
|
+
if (reported)
|
|
175
|
+
return reported;
|
|
176
|
+
return ErrorType.UNKNOWN;
|
|
177
|
+
}
|
|
178
|
+
export function normalizeCoreErrorReason(reason) {
|
|
179
|
+
const normalized = reason?.trim().toLowerCase();
|
|
180
|
+
switch (normalized) {
|
|
181
|
+
case 'retry.exhausted':
|
|
182
|
+
return 'retry_exhausted';
|
|
183
|
+
case 'sdk_timeout':
|
|
184
|
+
case 'total_execution_timeout':
|
|
185
|
+
return 'timeout';
|
|
186
|
+
case 'aborted_streaming':
|
|
187
|
+
case 'aborted_tools':
|
|
188
|
+
case 'stop':
|
|
189
|
+
return 'cancelled';
|
|
190
|
+
case 'protocol_incomplete':
|
|
191
|
+
case 'max_output_tokens':
|
|
192
|
+
case 'target_session_timeout':
|
|
193
|
+
case 'retry_exhausted':
|
|
194
|
+
case 'timeout':
|
|
195
|
+
case 'cancelled':
|
|
196
|
+
case 'permission_denied':
|
|
197
|
+
case 'input_already_submitted':
|
|
198
|
+
case 'fallback_exhausted':
|
|
199
|
+
case 'orphaned_target_session':
|
|
200
|
+
return normalized;
|
|
201
|
+
default:
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
94
205
|
/**
|
|
95
206
|
* 错误来源前缀 — 区分基础设施异常 vs Agent 任务失败
|
|
96
207
|
*
|
|
@@ -466,7 +577,7 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
466
577
|
}
|
|
467
578
|
}
|
|
468
579
|
// 回退到原有的错误消息匹配逻辑
|
|
469
|
-
const msg =
|
|
580
|
+
const msg = resolveErrorMessage(error);
|
|
470
581
|
const warnPrefix = includeEmoji ? '⚠️ ' : '';
|
|
471
582
|
const errPrefix = includeEmoji ? '❌ ' : '';
|
|
472
583
|
if (/invalid (?:jsonl )?session file/i.test(msg)) {
|
|
@@ -512,5 +623,36 @@ export function getErrorMessage(error, terminalReason, includeEmoji = true) {
|
|
|
512
623
|
if (msg.includes('timeout')) {
|
|
513
624
|
return `${warnPrefix}请求超时,请重试`;
|
|
514
625
|
}
|
|
626
|
+
if (msg)
|
|
627
|
+
return `${errPrefix}暂无法判定错误原因:${msg}`;
|
|
515
628
|
return `${errPrefix}处理消息时出错,请稍后重试`;
|
|
516
629
|
}
|
|
630
|
+
export function getToolResultErrorText(event) {
|
|
631
|
+
const text = event.error || event.reason
|
|
632
|
+
|| (event.name === 'Shell'
|
|
633
|
+
? typeof event.exitCode === 'number' && Number.isInteger(event.exitCode)
|
|
634
|
+
? 'exitCode: ' + event.exitCode
|
|
635
|
+
: '执行失败'
|
|
636
|
+
: typeof event.result === 'string' ? event.result : JSON.stringify(event.result))
|
|
637
|
+
|| '执行失败';
|
|
638
|
+
return text.replace(/<tool_use_error>(.*?)<\/tool_use_error>/gs, '$1');
|
|
639
|
+
}
|
|
640
|
+
function resolveErrorMessage(error) {
|
|
641
|
+
if (typeof error === 'string')
|
|
642
|
+
return error.trim();
|
|
643
|
+
if (error === null || error === undefined)
|
|
644
|
+
return '';
|
|
645
|
+
if (typeof error === 'object') {
|
|
646
|
+
const message = 'message' in error ? String(error.message ?? '').trim() : '';
|
|
647
|
+
if (message)
|
|
648
|
+
return message;
|
|
649
|
+
const code = 'code' in error ? String(error.code ?? '').trim() : '';
|
|
650
|
+
if (code)
|
|
651
|
+
return code;
|
|
652
|
+
if ('message' in error)
|
|
653
|
+
return '';
|
|
654
|
+
const stringified = String(error).trim();
|
|
655
|
+
return stringified === '[object Object]' || stringified === 'Error' ? '' : stringified;
|
|
656
|
+
}
|
|
657
|
+
return String(error).trim();
|
|
658
|
+
}
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { spawnSync } from 'child_process';
|
|
14
|
-
import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
15
|
-
import { isProcessRunning, killProcess, isWindows,
|
|
14
|
+
import { getPackageRoot, resolvePaths, isDevMode } from '../paths.js';
|
|
15
|
+
import { isProcessRunning, killProcess, isWindows, findProcessesWithCommandLine } from './cross-platform.js';
|
|
16
16
|
import { getProcessStartTime, startTimeMatches } from './process-introspect.js';
|
|
17
17
|
import { isConfirmedLeakedTestDaemon, runtimeHomeFromEnv } from './restart-safety.js';
|
|
18
18
|
import { decodeWindowsOutput } from './windows-output.js';
|
|
@@ -55,6 +55,7 @@ export function writeMain(launchedBy) {
|
|
|
55
55
|
startedAt,
|
|
56
56
|
startedAtIso: new Date(startedAt).toISOString(),
|
|
57
57
|
launchedBy,
|
|
58
|
+
runMode: isDevMode() ? 'dev' : 'install',
|
|
58
59
|
};
|
|
59
60
|
const filePath = path.join(dir, mainFileName(process.pid));
|
|
60
61
|
writeAtomic(filePath, JSON.stringify(record, null, 2));
|
|
@@ -376,10 +377,10 @@ export function isEvolCoreMainCommand(cmdline, packageRoot = getPackageRoot()) {
|
|
|
376
377
|
*
|
|
377
378
|
* 不会主动 kill——清理由调用方决定。
|
|
378
379
|
*/
|
|
379
|
-
export function findOrphanProcesses() {
|
|
380
|
-
// 1. 已登记 PID(自己 HOME 下的 main +
|
|
380
|
+
export function findOrphanProcesses(status = scanInstances()) {
|
|
381
|
+
// 1. 已登记 PID(自己 HOME 下的 main + 自己进程)。调用方刚扫过登记簿时可直接传入,
|
|
382
|
+
// Windows 上每次 scanInstances 都要为活实例跑一遍 CIM 查询。
|
|
381
383
|
const known = new Set([process.pid]);
|
|
382
|
-
const status = scanInstances();
|
|
383
384
|
for (const m of status.mains) {
|
|
384
385
|
if (m.alive)
|
|
385
386
|
known.add(m.record.pid);
|
|
@@ -393,14 +394,18 @@ export function findOrphanProcesses() {
|
|
|
393
394
|
// regex contains escaped separators and is applied after command lines have
|
|
394
395
|
// been fetched; embedding it in the CIM pre-filter can silently miss a
|
|
395
396
|
// backslash-delimited `dist\\index.js` command on some PowerShell builds.
|
|
396
|
-
|
|
397
|
+
// `index.js` 会命中所有 Node 服务(各会话拉起的 MCP server 常有十几个),
|
|
398
|
+
// 所以先用快照里的命令行过滤,只对真正的 EvolCore 入口做后续探测。
|
|
399
|
+
const candidates = findProcessesWithCommandLine('index\\.js');
|
|
397
400
|
const orphans = [];
|
|
398
|
-
for (const pid of candidates) {
|
|
401
|
+
for (const { pid, commandLine } of candidates) {
|
|
399
402
|
if (known.has(pid))
|
|
400
403
|
continue;
|
|
404
|
+
if (commandLine !== undefined && !isEvolCoreMainCommand(commandLine))
|
|
405
|
+
continue;
|
|
401
406
|
if (!isProcessRunning(pid))
|
|
402
407
|
continue;
|
|
403
|
-
const cmdline = readCmdline(pid);
|
|
408
|
+
const cmdline = commandLine || readCmdline(pid);
|
|
404
409
|
// 二次验证:入口必须是当前 EvolCore 包的 dist/index.js。
|
|
405
410
|
if (!isEvolCoreMainCommand(cmdline))
|
|
406
411
|
continue;
|