evolcore 0.0.26 → 0.0.28
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 +36 -0
- package/bin/codex-managed-acl.ps1 +43 -0
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +10 -5
- package/dist/agents/claude-runner.js +123 -22
- package/dist/agents/codex/app-server-client.js +70 -26
- package/dist/agents/codex/catalog.js +7 -1
- package/dist/agents/codex-runner.js +363 -421
- package/dist/agents/ecagent-runner.js +170 -25
- package/dist/agents/gemini-runner.js +6 -0
- package/dist/agents/runner-types.js +50 -2
- package/dist/aun/aid/agentmd.js +73 -31
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +3 -3
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +161 -38
- package/dist/aun/{menu-client.js → menu/client.js} +13 -7
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +84 -18
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +13 -8
- package/dist/aun/msg/upload.js +43 -88
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +544 -239
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +108 -6
- package/dist/cli/agent-create.js +515 -2
- package/dist/cli/agent.js +109 -8
- package/dist/cli/aun-commands.js +191 -42
- package/dist/cli/config.js +8 -5
- package/dist/cli/contact.js +2 -2
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +111 -162
- package/dist/cli/diag.js +3 -3
- package/dist/cli/fs-command.js +3 -3
- package/dist/cli/handoff-command.js +6 -6
- package/dist/cli/index.js +8 -2
- package/dist/cli/init.js +34 -1
- package/dist/cli/managed-command-guard.js +2 -1
- package/dist/cli/menu-client.js +13 -5
- package/dist/cli/model.js +3 -3
- package/dist/cli/queue-command.js +2 -2
- package/dist/cli/response.js +2 -2
- package/dist/cli/restart-monitor.js +81 -19
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +20 -10
- package/dist/config/builtin-roles.js +10 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +20 -5
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +1 -1
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +5 -0
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +34 -38
- package/dist/core/auth/operation-authorizer.js +67 -5
- package/dist/core/auth/operation-catalog.js +110 -0
- package/dist/core/auth/remote-access-store.js +115 -9
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +110 -19
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +105 -40
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +292 -27
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +34 -4
- package/dist/core/command/menu-handler.js +364 -96
- package/dist/core/command/menu-protocol.js +43 -6
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-handler.js +61 -37
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +94 -16
- package/dist/core/message/message-bridge.js +140 -13
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +73 -41
- package/dist/core/message/response-engine.js +729 -118
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +76 -36
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +72 -3
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +8 -8
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +38 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +322 -207
- package/dist/ipc.js +141 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +3 -0
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/atomic-write.js +98 -0
- package/dist/utils/cross-platform.js +87 -16
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-utils.js +40 -2
- package/dist/utils/logger.js +23 -13
- package/dist/utils/npm-ops.js +119 -4
- package/dist/utils/package-version-info.js +244 -0
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +174 -1
- package/dist/utils/windows-shell-trust.js +37 -0
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/INDEX.md +0 -6
- package/kits/docs/channels/aun.md +23 -2
- package/kits/docs/channels/feishu.md +1 -1
- package/kits/docs/context-assembly.md +29 -17
- package/kits/docs/evolcore/INDEX.md +8 -3
- package/kits/docs/evolcore/agent-create.md +39 -7
- package/kits/docs/evolcore/agent.md +33 -1
- package/kits/docs/evolcore/aid.md +9 -1
- package/kits/docs/evolcore/config.md +40 -1
- package/kits/docs/evolcore/group-collaboration.md +1 -1
- package/kits/docs/evolcore/group.md +117 -2
- package/kits/docs/evolcore/menu-client.md +16 -0
- package/kits/docs/evolcore/msg.md +3 -0
- package/kits/docs/evolcore/storage.md +3 -0
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +51 -3
- package/kits/docs/venues/group.md +5 -0
- package/kits/eck_manifest.json +27 -0
- package/kits/rules/01-overview.md +11 -7
- package/kits/rules/04-relation.md +2 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +6 -4
- package/kits/schemas/agent-config.schema.12.json +25 -1
- package/kits/schemas/daemon.schema.6.json +2 -2
- package/kits/schemas/defaults.schema.5.json +1 -1
- package/kits/schemas/relation-config.schema.1.json +1 -1
- package/kits/schemas/relation-config.schema.5.json +1 -1
- package/kits/schemas/role-config.schema.1.json +14 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/baseagent.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +26 -9
- package/kits/templates/system-fragments/channel.md +8 -44
- package/kits/templates/system-fragments/commands.md +10 -2
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/reply.md +56 -0
- package/package.json +7 -3
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { loadAgent } from '../../config-store.js';
|
|
2
|
+
import { initializationBlocksTasks } from '../../cli/agent-create.js';
|
|
1
3
|
import { createHash, randomBytes } from 'crypto';
|
|
4
|
+
import { agentStorageMenuService, isAgentStorageMenuRequest } from '../command/agent-storage-menu.js';
|
|
2
5
|
import { logger } from '../../utils/logger.js';
|
|
3
6
|
import { StreamDebouncer } from './stream-debouncer.js';
|
|
4
7
|
import { appendMessageLog, appendMessageLogStrict, buildInboundEntry, isTransientProtocolMessage } from './message-log.js';
|
|
5
8
|
import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from './message-utils.js';
|
|
6
9
|
import { chatDirPath } from '../session/session-fs-store.js';
|
|
10
|
+
import { readTopicLable } from '../session/session-mapper.js';
|
|
7
11
|
import { formatChannelKey, tryParseChannelKey } from '../channel-loader.js';
|
|
8
12
|
import { agentDir, resolvePaths } from '../../paths.js';
|
|
9
13
|
import { listStaticAgentAdmins, listStaticAgentOwners, resolvePeerRoleDetail, } from '../../config/peer-role-resolver.js';
|
|
@@ -27,7 +31,9 @@ import { renderActionAsText } from '../interaction-router.js';
|
|
|
27
31
|
import { planApprovalRoute } from '../permission/approval-gateway.js';
|
|
28
32
|
import { guardGroupSlashCommand, parseFullAccessCommand } from '../command/slash-gate.js';
|
|
29
33
|
import { isFullAccessEnabled } from '../../config-store.js';
|
|
34
|
+
import { createAgentPolicyAuthorization } from '../role/runtime-policy.js';
|
|
30
35
|
import { auditFullAccessEvent } from '../auth/authorization-audit.js';
|
|
36
|
+
import { DIAGNOSTIC_AGENT_AID, DiagnosticSessionRouter, parseDiagnosticEvent, isDiagnosticSessionActive } from '../command/diag-service.js';
|
|
31
37
|
import { consumeAdmissionReplyBudget, clearAdmissionReplyBudgets, evaluateInboundAdmissionPreflight, evaluateOrdinaryInboundAdmission, } from './message-admission.js';
|
|
32
38
|
const CONTACT_REJECTION_NOTICE = '[rejection] Not accepting messages from you.';
|
|
33
39
|
const REJECTABLE_PEER_TYPES = new Set(['ai', 'bot', 'agent', 'service']);
|
|
@@ -110,9 +116,11 @@ export class MessageBridge {
|
|
|
110
116
|
menuTokenStore = new MenuTokenStore();
|
|
111
117
|
handoffRuntime;
|
|
112
118
|
sessionRenewService;
|
|
119
|
+
diagnosticRouter;
|
|
113
120
|
aidStatsCollector;
|
|
114
121
|
contactBindRuntimeChecker;
|
|
115
122
|
interactionRouter;
|
|
123
|
+
groupInviteReviewService;
|
|
116
124
|
constructor(defaultProjectPath, sessionManager, processor, messageQueue, cmdHandler, eventBus, defaultDebounce, processOwnersProvider) {
|
|
117
125
|
this.defaultProjectPath = defaultProjectPath;
|
|
118
126
|
this.sessionManager = sessionManager;
|
|
@@ -123,6 +131,13 @@ export class MessageBridge {
|
|
|
123
131
|
this.processOwnersProvider = processOwnersProvider;
|
|
124
132
|
this.defaultDebounce = defaultDebounce ?? 0;
|
|
125
133
|
this.sessionRenewService = new SessionRenewService(sessionManager, processor, { eventBus });
|
|
134
|
+
this.diagnosticRouter = new DiagnosticSessionRouter(sessionManager, async (sessionId) => {
|
|
135
|
+
// Revoke in the router first, then clear pending work before awaiting the
|
|
136
|
+
// runner interruption. Delayed debounce and recovery are fenced below.
|
|
137
|
+
this.messageQueue.clearBySession(sessionId);
|
|
138
|
+
this.messageQueue.markActiveTerminal(sessionId, 'stop');
|
|
139
|
+
await this.processor.interruptSession(sessionId, 'stop');
|
|
140
|
+
});
|
|
126
141
|
}
|
|
127
142
|
/** Inject EvolAgentRegistry so owner lookups/writes route to agent.json for agent-owned channels. */
|
|
128
143
|
setAgentRegistry(registry) {
|
|
@@ -144,6 +159,12 @@ export class MessageBridge {
|
|
|
144
159
|
setInteractionRouter(router) {
|
|
145
160
|
this.interactionRouter = router;
|
|
146
161
|
}
|
|
162
|
+
setGroupInviteReviewService(service) {
|
|
163
|
+
this.groupInviteReviewService = service;
|
|
164
|
+
}
|
|
165
|
+
registerGroupInviteReviewChannel(binding) {
|
|
166
|
+
this.groupInviteReviewService?.attach(binding);
|
|
167
|
+
}
|
|
147
168
|
/**
|
|
148
169
|
* AUN supplies an authenticated sender AID, while the IM-side code proves
|
|
149
170
|
* possession of the external account. This command only creates that
|
|
@@ -483,7 +504,7 @@ export class MessageBridge {
|
|
|
483
504
|
...(msg.replyContext ?? {}),
|
|
484
505
|
metadata: {
|
|
485
506
|
...(msg.replyContext?.metadata ?? {}),
|
|
486
|
-
...evolMenuResponseTransportMetadata(),
|
|
507
|
+
...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version),
|
|
487
508
|
},
|
|
488
509
|
},
|
|
489
510
|
}), {
|
|
@@ -795,6 +816,20 @@ export class MessageBridge {
|
|
|
795
816
|
}
|
|
796
817
|
return;
|
|
797
818
|
}
|
|
819
|
+
if (!trustedHandoffEcho && selfAid && actorId && this.groupInviteReviewService) {
|
|
820
|
+
const inviteCommand = await this.groupInviteReviewService.handleCommand({
|
|
821
|
+
selfAid,
|
|
822
|
+
ownerAid: actorId,
|
|
823
|
+
channelKey,
|
|
824
|
+
channelType: resolvedChannelType,
|
|
825
|
+
chatType,
|
|
826
|
+
content,
|
|
827
|
+
});
|
|
828
|
+
if (inviteCommand.handled) {
|
|
829
|
+
await this.sendAdmissionControlReply(admissionPreflight.context, msg, sendReply, inviteCommand.text ?? '群邀请命令已处理。');
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
798
833
|
if (!trustedHandoffEcho && await this.handleReservedContactControl({
|
|
799
834
|
context: admissionPreflight.context,
|
|
800
835
|
msg,
|
|
@@ -945,6 +980,37 @@ export class MessageBridge {
|
|
|
945
980
|
await this.sendAccessDenied(adapter, channelName, msg, sendReply);
|
|
946
981
|
return;
|
|
947
982
|
}
|
|
983
|
+
// Diagnostic invitations carry their own execution boundary. Resolve
|
|
984
|
+
// that boundary before ordinary command/session routing so a new case
|
|
985
|
+
// cannot inherit an older Codex thread.
|
|
986
|
+
let diagnosticSession;
|
|
987
|
+
if (selfAid === DIAGNOSTIC_AGENT_AID && resolvedChannelType === 'aun'
|
|
988
|
+
&& chatType === 'private' && msg.source !== 'handoff') {
|
|
989
|
+
try {
|
|
990
|
+
const diagnosticEvent = parseDiagnosticEvent(content);
|
|
991
|
+
if (diagnosticEvent) {
|
|
992
|
+
releaseAdmission = await this.acquireAdmission(admissionRoute);
|
|
993
|
+
const routed = await this.diagnosticRouter.route({
|
|
994
|
+
event: diagnosticEvent, message: msg, channel: channelName,
|
|
995
|
+
channelType: resolvedChannelType, selfAid,
|
|
996
|
+
chatDir: chatDirPath(resolvePaths().sessionsDir, resolvedChannelType, msg.channelId, selfAid, channelName),
|
|
997
|
+
projectPath: owningAgent?.projectPath ?? this.defaultProjectPath,
|
|
998
|
+
baseagent: owningAgent?.baseagent,
|
|
999
|
+
});
|
|
1000
|
+
if ('notice' in routed) {
|
|
1001
|
+
await sendReply(msg.channelId, routed.notice, msg.replyContext);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
diagnosticSession = routed.session;
|
|
1005
|
+
diagnosticSession.identity = identity;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
logger.warn(`[MessageBridge] Diagnostic admission failed: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
1010
|
+
await sendReply(msg.channelId, '诊断会话隔离未完成,本次未执行诊断。请检查诊断端日志后重试。', msg.replyContext);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
948
1014
|
// /fa is an execution prefix, not a session-mode switch or a normal
|
|
949
1015
|
// quick command. Only this trusted bridge path can construct the
|
|
950
1016
|
// internal per-call authorization carried into ResponseEngine.
|
|
@@ -1027,6 +1093,11 @@ export class MessageBridge {
|
|
|
1027
1093
|
...(chatType === 'private' ? { routeId: msg.channelId } : {}),
|
|
1028
1094
|
});
|
|
1029
1095
|
}
|
|
1096
|
+
const initializationConfig = selfAid ? loadAgent(selfAid) : null;
|
|
1097
|
+
if (initializationConfig && initializationBlocksTasks(initializationConfig)) {
|
|
1098
|
+
await sendReply(msg.channelId, 'Agent 初始化尚未完成,请由创建者查询 ec agent show 或恢复初始化。');
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1030
1101
|
// 1. owner 绑定(按实例名绑定)
|
|
1031
1102
|
if (adapter && actorId && roleDetail.effectiveRole === 'owner') {
|
|
1032
1103
|
await this.bootstrapService?.tryStartBootstrap({
|
|
@@ -1106,6 +1177,14 @@ export class MessageBridge {
|
|
|
1106
1177
|
return sendReply(msg.channelId, text, msg.replyContext);
|
|
1107
1178
|
}, msg.peerId, msg.threadId, msg.chatType, msg.source, msg.replyContext, msg.messageId, msg.selfAID, authSubject))
|
|
1108
1179
|
return;
|
|
1180
|
+
// Commands retain their normal route. Only authenticated direct model
|
|
1181
|
+
// input can obtain the Agent policy's per-call authorization.
|
|
1182
|
+
if (!executionPermissionOverride && selfAid && owningAgent?.aid === selfAid) {
|
|
1183
|
+
executionPermissionOverride = createAgentPolicyAuthorization({
|
|
1184
|
+
agentAid: selfAid, actor: roleDetail.actor, chatType,
|
|
1185
|
+
source: msg.source, peerType: msg.peerType,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1109
1188
|
// 3. session 解析(使用 Channel 层填充的 chatType)
|
|
1110
1189
|
if (!(await this.canCreateThreadSession(channelName, msg, chatType))) {
|
|
1111
1190
|
// 静默丢弃:绝不向群里注入回复。
|
|
@@ -1147,9 +1226,10 @@ export class MessageBridge {
|
|
|
1147
1226
|
// transaction. Without this per-route single-flight, a second message
|
|
1148
1227
|
// can observe the just-renewed empty session before the first message
|
|
1149
1228
|
// has been appended and create another renewed session.
|
|
1150
|
-
|
|
1229
|
+
if (!releaseAdmission)
|
|
1230
|
+
releaseAdmission = await this.acquireAdmission(admissionRoute);
|
|
1151
1231
|
const hadMainSession = msg.threadId ? true : this.sessionManager.hasMainSession(channelName, msg.channelId, msg.channelType || effectiveChannelType, msg.selfAID || selfAid);
|
|
1152
|
-
let session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity);
|
|
1232
|
+
let session = diagnosticSession ?? await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity, readTopicLable(msg) ?? readTopicLable(msg.replyContext?.metadata));
|
|
1153
1233
|
if (session.threadId && typeof this.messageQueue.reserveSessionIngress === 'function') {
|
|
1154
1234
|
releaseSessionIngress = this.messageQueue.reserveSessionIngress(session.id);
|
|
1155
1235
|
}
|
|
@@ -1162,7 +1242,7 @@ export class MessageBridge {
|
|
|
1162
1242
|
const inboundCausation = restoredCausation
|
|
1163
1243
|
? deriveCausation(restoredCausation)
|
|
1164
1244
|
: createRootCausation();
|
|
1165
|
-
const renewResult = await this.sessionRenewService.resolve({
|
|
1245
|
+
const renewResult = session.metadata?.diagnostic ? { session } : await this.sessionRenewService.resolve({
|
|
1166
1246
|
session,
|
|
1167
1247
|
channelName,
|
|
1168
1248
|
channelType: msg.channelType || effectiveChannelType,
|
|
@@ -1205,6 +1285,7 @@ export class MessageBridge {
|
|
|
1205
1285
|
mentions: msg.mentions, mentionAids: msg.mentionAids, threadId: msg.threadId,
|
|
1206
1286
|
isMentioned: msg.isMentioned,
|
|
1207
1287
|
topicName: this.extractTopicName(msg),
|
|
1288
|
+
lable: session.lable,
|
|
1208
1289
|
replyContext: msg.replyContext,
|
|
1209
1290
|
source: msg.source,
|
|
1210
1291
|
...(executionPermissionOverride ? { executionPermissionOverride } : {}),
|
|
@@ -1298,6 +1379,9 @@ export class MessageBridge {
|
|
|
1298
1379
|
const enqueueAgentName = owningAgent?.name ?? '<unknown>';
|
|
1299
1380
|
const selfAID = session.selfAID;
|
|
1300
1381
|
const doEnqueue = async (m) => {
|
|
1382
|
+
// A cancellation can arrive while this message is being debounced.
|
|
1383
|
+
if (!isDiagnosticSessionActive(session, this.sessionManager.getChatDir(session)))
|
|
1384
|
+
return;
|
|
1301
1385
|
const enqueue = handoffCandidate ? this.messageQueue.enqueuePersisted.bind(this.messageQueue) : this.messageQueue.enqueue.bind(this.messageQueue);
|
|
1302
1386
|
try {
|
|
1303
1387
|
return await enqueue(session.id, m, session.projectPath, {
|
|
@@ -1427,6 +1511,8 @@ export class MessageBridge {
|
|
|
1427
1511
|
const menuAuthSubject = {
|
|
1428
1512
|
...authSubject,
|
|
1429
1513
|
requestId: parsed.id,
|
|
1514
|
+
menuEncrypted: msg.encrypted === true,
|
|
1515
|
+
fromControlChannel: authSubject.fromControlChannel || msg.isControlChannel === true,
|
|
1430
1516
|
...(activeSession?.id ? { sessionId: activeSession.id } : {}),
|
|
1431
1517
|
};
|
|
1432
1518
|
const isTokenRequest = parsed.type === 'menu.token.request';
|
|
@@ -1457,6 +1543,17 @@ export class MessageBridge {
|
|
|
1457
1543
|
return;
|
|
1458
1544
|
}
|
|
1459
1545
|
}
|
|
1546
|
+
// Reauthorize even cached responses: roots expire and static owners can be revoked.
|
|
1547
|
+
const isStorageRequest = isAgentStorageMenuRequest(parsed.request);
|
|
1548
|
+
if (isStorageRequest) {
|
|
1549
|
+
try {
|
|
1550
|
+
agentStorageMenuService.preflight(parsed.request, menuAuthSubject);
|
|
1551
|
+
}
|
|
1552
|
+
catch (error) {
|
|
1553
|
+
await this.sendMenuResponse(adapter, channel, msg, menuFailure(header, normalizeMenuError(error)));
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1460
1557
|
const scope = msg.chatType === 'group' ? (msg.groupId || msg.channelId) : msg.peerId;
|
|
1461
1558
|
// Separate installations can generate identical request IDs. Token
|
|
1462
1559
|
// issuance and replay must remain scoped to the authenticated requester.
|
|
@@ -1474,6 +1571,16 @@ export class MessageBridge {
|
|
|
1474
1571
|
}));
|
|
1475
1572
|
return;
|
|
1476
1573
|
}
|
|
1574
|
+
// A replay may have awaited an in-flight request; check again before release.
|
|
1575
|
+
if (isStorageRequest) {
|
|
1576
|
+
try {
|
|
1577
|
+
agentStorageMenuService.preflight(parsed.request, menuAuthSubject, false);
|
|
1578
|
+
}
|
|
1579
|
+
catch (error) {
|
|
1580
|
+
await this.sendMenuResponse(adapter, channel, msg, menuFailure(header, normalizeMenuError(error)));
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1477
1584
|
if (deduped.replayed)
|
|
1478
1585
|
this.logMenuDiagnostic('request-replay', channel, msg, parsed);
|
|
1479
1586
|
if (deduped.retry)
|
|
@@ -1488,7 +1595,20 @@ export class MessageBridge {
|
|
|
1488
1595
|
await deferredEffect();
|
|
1489
1596
|
}
|
|
1490
1597
|
catch (error) {
|
|
1491
|
-
|
|
1598
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1599
|
+
const code = error && typeof error === 'object' && 'code' in error
|
|
1600
|
+
? String(error.code ?? 'EXEC_FAILED')
|
|
1601
|
+
: 'EXEC_FAILED';
|
|
1602
|
+
logger.warn(`[MenuControl] deferred effect failed: request=${parsed.id} error=${detail}`);
|
|
1603
|
+
// File fetch is acknowledged before the upload completes. Send a
|
|
1604
|
+
// second response with the same request id so the client can finish
|
|
1605
|
+
// the pending fetch with an actionable error instead of timing out.
|
|
1606
|
+
const failedResponse = menuFailure(header, {
|
|
1607
|
+
code,
|
|
1608
|
+
message: detail.startsWith('文件发送失败') ? detail : `文件发送失败: ${detail}`,
|
|
1609
|
+
});
|
|
1610
|
+
await this.menuDeduper.replaceResult(dedupKey, menuPayloadFingerprint(parsed.raw), deduped.value, failedResponse);
|
|
1611
|
+
await this.sendMenuResponse(adapter, channel, msg, failedResponse);
|
|
1492
1612
|
}
|
|
1493
1613
|
}
|
|
1494
1614
|
}
|
|
@@ -1498,6 +1618,13 @@ export class MessageBridge {
|
|
|
1498
1618
|
const name = header.name ?? '';
|
|
1499
1619
|
const cmd = typeof request.cmd === 'string' ? request.cmd : undefined;
|
|
1500
1620
|
const args = request.args;
|
|
1621
|
+
const transportReplyContext = {
|
|
1622
|
+
...(msg.replyContext ?? {}),
|
|
1623
|
+
...(msg.threadId ? { threadId: msg.threadId } : {}),
|
|
1624
|
+
};
|
|
1625
|
+
// Explicit group context can target a different conversation. Its
|
|
1626
|
+
// sessions must not inherit the thread of the source conversation.
|
|
1627
|
+
const replyContextFor = (channelId) => channelId === msg.channelId ? transportReplyContext : undefined;
|
|
1501
1628
|
switch (request.type) {
|
|
1502
1629
|
case 'menu.list':
|
|
1503
1630
|
if (args?.view === 'catalog') {
|
|
@@ -1514,7 +1641,7 @@ export class MessageBridge {
|
|
|
1514
1641
|
case 'menu.query': {
|
|
1515
1642
|
if (name === 'menu' && args?.view === 'values') {
|
|
1516
1643
|
validateMenuValueBatchArgs(args);
|
|
1517
|
-
const data = await this.cmdHandler.execMenuValueBatch(channel, msg.channelId, authSubject, args, args.context, msg.isControlChannel ? 'control' : 'agent');
|
|
1644
|
+
const data = await this.cmdHandler.execMenuValueBatch(channel, msg.channelId, authSubject, args, args.context, msg.isControlChannel ? 'control' : 'agent', transportReplyContext);
|
|
1518
1645
|
return menuSuccess(header, data);
|
|
1519
1646
|
}
|
|
1520
1647
|
if (name === 'menu') {
|
|
@@ -1527,7 +1654,7 @@ export class MessageBridge {
|
|
|
1527
1654
|
channelId: msg.channelId,
|
|
1528
1655
|
args,
|
|
1529
1656
|
});
|
|
1530
|
-
const result = await this.cmdHandler.execMenuQuery(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject.identity, execution.subject);
|
|
1657
|
+
const result = await this.cmdHandler.execMenuQuery(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, execution.args, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject.identity, execution.subject, 'menu', replyContextFor(execution.channelId));
|
|
1531
1658
|
if ('error' in result)
|
|
1532
1659
|
throw result;
|
|
1533
1660
|
return menuSuccess(header, result.data);
|
|
@@ -1544,7 +1671,7 @@ export class MessageBridge {
|
|
|
1544
1671
|
if ((name === 'topic' || cmd === '/topic') && args?.mode === 'fork-turns' && msg.threadId && !args?.sourceThreadId) {
|
|
1545
1672
|
optionArgs = { ...(execution.args ?? {}), sourceThreadId: msg.threadId };
|
|
1546
1673
|
}
|
|
1547
|
-
const data = await this.cmdHandler.getSubMenuItems(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, optionArgs, execution.subject.identity, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject) ?? [];
|
|
1674
|
+
const data = await this.cmdHandler.getSubMenuItems(this.resolveCmd(name, cmd), channel, execution.channelId, execution.subject.actorId, optionArgs, execution.subject.identity, execution.subject.chatType, execution.subject.fromControlChannel, execution.subject, 'menu', replyContextFor(execution.channelId)) ?? [];
|
|
1548
1675
|
return menuSuccess(header, data);
|
|
1549
1676
|
}
|
|
1550
1677
|
case 'menu.update': {
|
|
@@ -1555,10 +1682,7 @@ export class MessageBridge {
|
|
|
1555
1682
|
channelId: msg.channelId,
|
|
1556
1683
|
args,
|
|
1557
1684
|
});
|
|
1558
|
-
const result = await this.cmdHandler.execMenuUpdate(this.resolveCmd(name, cmd), request.value, channel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.fromControlChannel, execution.args, execution.subject, 'menu',
|
|
1559
|
-
...(msg.replyContext ?? {}),
|
|
1560
|
-
...(msg.threadId ? { threadId: msg.threadId } : {}),
|
|
1561
|
-
});
|
|
1685
|
+
const result = await this.cmdHandler.execMenuUpdate(this.resolveCmd(name, cmd), request.value, channel, execution.channelId, execution.subject.actorId, execution.subject.identity, execution.subject.fromControlChannel, execution.args, execution.subject, 'menu', replyContextFor(execution.channelId));
|
|
1562
1686
|
if ('error' in result)
|
|
1563
1687
|
throw result;
|
|
1564
1688
|
return menuSuccess(header, result.data);
|
|
@@ -1621,9 +1745,11 @@ export class MessageBridge {
|
|
|
1621
1745
|
const agentName = this.agentRegistry?.resolveByChannel(channel)?.name ?? '<unknown>';
|
|
1622
1746
|
const replyContext = {
|
|
1623
1747
|
...(msg.replyContext ?? {}),
|
|
1748
|
+
...(msg.peerDeviceId ? { toDeviceId: msg.peerDeviceId } : {}),
|
|
1624
1749
|
metadata: {
|
|
1625
1750
|
...(msg.replyContext?.metadata ?? {}),
|
|
1626
|
-
...evolMenuResponseTransportMetadata(),
|
|
1751
|
+
...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version),
|
|
1752
|
+
...(isAgentStorageMenuRequest(request) ? { encrypted: true } : {}),
|
|
1627
1753
|
},
|
|
1628
1754
|
};
|
|
1629
1755
|
if (msg.channelType === 'aun' && !replyContext.delivery) {
|
|
@@ -1890,6 +2016,7 @@ export class MessageBridge {
|
|
|
1890
2016
|
}
|
|
1891
2017
|
/** 注销单个渠道的 debouncer(热重载断开渠道时调用) */
|
|
1892
2018
|
removeChannel(channelName) {
|
|
2019
|
+
this.groupInviteReviewService?.detach(channelName);
|
|
1893
2020
|
const d = this.debouncers.get(channelName);
|
|
1894
2021
|
if (d) {
|
|
1895
2022
|
d.dispose();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { appendJsonl, chatDirPath } from '../session/session-fs-store.js';
|
|
3
|
+
import { appendJsonl, appendJsonlDurable, chatDirPath } from '../session/session-fs-store.js';
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
const TRANSIENT_PROTOCOL_TYPE_PREFIXES = [
|
|
6
6
|
'contact.request.',
|
|
@@ -313,13 +313,13 @@ export function appendMessageLog(chatDir, entry) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
/** Strict conversation-history append used by handoff reply binding. */
|
|
316
|
-
export function appendMessageLogStrict(chatDir, entry) {
|
|
316
|
+
export function appendMessageLogStrict(chatDir, entry, durable = false) {
|
|
317
317
|
if (isTransientProtocolMessage(entry))
|
|
318
318
|
return;
|
|
319
319
|
if (entry.dir === 'in' && hasDuplicate(chatDir, entry.msgId))
|
|
320
320
|
return;
|
|
321
321
|
fs.mkdirSync(chatDir, { recursive: true });
|
|
322
|
-
appendJsonl(messageLogPath(chatDir), entry);
|
|
322
|
+
(durable ? appendJsonlDurable : appendJsonl)(messageLogPath(chatDir), entry);
|
|
323
323
|
if (entry.dir === 'in')
|
|
324
324
|
rememberMessageId(chatDir, entry.msgId);
|
|
325
325
|
}
|
|
@@ -343,6 +343,8 @@ export function buildInboundEntry(opts) {
|
|
|
343
343
|
msgType: isCommand ? 'command' : (opts.msgType ?? 'text'),
|
|
344
344
|
payloadType: opts.payloadType,
|
|
345
345
|
payloadSummary: opts.payloadSummary,
|
|
346
|
+
groupAttention: opts.groupAttention,
|
|
347
|
+
groupDelivery: opts.groupDelivery,
|
|
346
348
|
content: opts.content,
|
|
347
349
|
replyTo: opts.replyTo ?? null,
|
|
348
350
|
agent: null,
|
|
@@ -4,6 +4,7 @@ import { logger } from '../../utils/logger.js';
|
|
|
4
4
|
import { LogicalQueueBridge } from './logical-queue-bridge.js';
|
|
5
5
|
import { normalizeCausation } from '../causation/context.js';
|
|
6
6
|
import { KeyedFairMutex } from '../handoff/mutex.js';
|
|
7
|
+
import { ExecutionTimeoutError, OrphanedTargetSessionError, isExecutionFailure } from '../../utils/error-utils.js';
|
|
7
8
|
const DEFAULT_AGENT_NAME = '<unknown>';
|
|
8
9
|
const RESTART_RESUME_CONTENT = 'evolcore 服务已重启,请继续之前未完成的任务。';
|
|
9
10
|
export class MessageQueue {
|
|
@@ -52,7 +53,7 @@ export class MessageQueue {
|
|
|
52
53
|
setInterruptCallback(callback) {
|
|
53
54
|
this.interruptCallback = callback;
|
|
54
55
|
}
|
|
55
|
-
triggerInterrupt(queueKey, sessionKey, baseagent, evolagentName, reason) {
|
|
56
|
+
triggerInterrupt(queueKey, sessionKey, baseagent, evolagentName, reason, timeout) {
|
|
56
57
|
const existing = this.interruptBarriers.get(queueKey);
|
|
57
58
|
if (existing)
|
|
58
59
|
return existing;
|
|
@@ -61,7 +62,9 @@ export class MessageQueue {
|
|
|
61
62
|
this.activeTerminalReasons.set(queueKey, reason);
|
|
62
63
|
let interruption;
|
|
63
64
|
try {
|
|
64
|
-
interruption = Promise.resolve(
|
|
65
|
+
interruption = Promise.resolve(timeout
|
|
66
|
+
? this.interruptCallback(sessionKey, baseagent, evolagentName, reason, timeout)
|
|
67
|
+
: this.interruptCallback(sessionKey, baseagent, evolagentName, reason));
|
|
65
68
|
}
|
|
66
69
|
catch (error) {
|
|
67
70
|
interruption = Promise.reject(error);
|
|
@@ -980,7 +983,8 @@ export class MessageQueue {
|
|
|
980
983
|
this.persistQueuesImmediate();
|
|
981
984
|
}
|
|
982
985
|
else if (message.messageId) {
|
|
983
|
-
|
|
986
|
+
const timeout = isExecutionFailure(options?.signal?.reason) ? options.signal.reason : undefined;
|
|
987
|
+
void this.cancelActiveAndWait(message.messageId, message.selfAID, timeout).finally(() => {
|
|
984
988
|
this.forgetProcessed(sessionKey, message);
|
|
985
989
|
settleReject(error);
|
|
986
990
|
});
|
|
@@ -1262,7 +1266,9 @@ export class MessageQueue {
|
|
|
1262
1266
|
return result;
|
|
1263
1267
|
}
|
|
1264
1268
|
isMergeBarrier(message) {
|
|
1265
|
-
return !!message.
|
|
1269
|
+
return !!message.taskRecovery
|
|
1270
|
+
|| !!message.asyncQuestionAnswer
|
|
1271
|
+
|| !!message.executionPermissionOverride
|
|
1266
1272
|
|| !!message.handoffDelivery
|
|
1267
1273
|
|| !!message.triggerMeta
|
|
1268
1274
|
|| !!message.causation?.trigger?.path.length;
|
|
@@ -1525,12 +1531,14 @@ export class MessageQueue {
|
|
|
1525
1531
|
return cancellation.found;
|
|
1526
1532
|
}
|
|
1527
1533
|
/** Cancel an active message and wait until the runner interrupt barrier settles. */
|
|
1528
|
-
async cancelActiveAndWait(messageId, selfAID) {
|
|
1529
|
-
const cancellation = this.beginActiveCancellation(messageId, selfAID);
|
|
1534
|
+
async cancelActiveAndWait(messageId, selfAID, cause) {
|
|
1535
|
+
const cancellation = this.beginActiveCancellation(messageId, selfAID, cause);
|
|
1530
1536
|
await cancellation.barrier;
|
|
1531
1537
|
return cancellation.found;
|
|
1532
1538
|
}
|
|
1533
|
-
beginActiveCancellation(messageId, selfAID) {
|
|
1539
|
+
beginActiveCancellation(messageId, selfAID, cause) {
|
|
1540
|
+
const reason = cause instanceof ExecutionTimeoutError ? 'timeout'
|
|
1541
|
+
: cause instanceof OrphanedTargetSessionError ? 'execution_error' : cause ?? 'recalled';
|
|
1534
1542
|
for (const [queueKey, activeState] of this.activeStates.entries()) {
|
|
1535
1543
|
if (!activeState.messageIds.has(messageId))
|
|
1536
1544
|
continue;
|
|
@@ -1558,18 +1566,20 @@ export class MessageQueue {
|
|
|
1558
1566
|
}
|
|
1559
1567
|
const sessionKey = this.sessionKeyFromQueueKey(queueKey);
|
|
1560
1568
|
const agentName = this.processingAgent.get(queueKey);
|
|
1561
|
-
logger.info(`[Queue]
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1569
|
+
logger.info(`[Queue] Cancelled active message ${messageId}, interrupting session ${sessionKey}: ${reason}`);
|
|
1570
|
+
// TimeoutController owns the failure event and outbound timeout terminal.
|
|
1571
|
+
if (reason !== 'timeout' && reason !== 'execution_error')
|
|
1572
|
+
this.eventBus?.publish({
|
|
1573
|
+
type: 'task:interrupted',
|
|
1574
|
+
sessionId: sessionKey,
|
|
1575
|
+
reason,
|
|
1576
|
+
agentName,
|
|
1577
|
+
taskId: activeState?.taskId,
|
|
1578
|
+
generation: activeState?.generation,
|
|
1579
|
+
causation: active?.message.causation,
|
|
1580
|
+
});
|
|
1571
1581
|
const barrier = this.interruptCallback
|
|
1572
|
-
? this.triggerInterrupt(queueKey, sessionKey, activeState.baseagent, agentName,
|
|
1582
|
+
? this.triggerInterrupt(queueKey, sessionKey, activeState.baseagent, agentName, reason, isExecutionFailure(cause) ? cause : undefined)
|
|
1573
1583
|
: Promise.resolve();
|
|
1574
1584
|
return { found: true, barrier };
|
|
1575
1585
|
}
|
|
@@ -1603,33 +1613,11 @@ export class MessageQueue {
|
|
|
1603
1613
|
}
|
|
1604
1614
|
let releasePromise;
|
|
1605
1615
|
const promise = new Promise(resolve => { releasePromise = resolve; });
|
|
1606
|
-
let queued = 0;
|
|
1607
|
-
let active = false;
|
|
1608
|
-
let dispatching = false;
|
|
1609
|
-
for (const [queueKey, queue] of this.queues.entries()) {
|
|
1610
|
-
if (!this.matchesSession(queueKey, sessionKey))
|
|
1611
|
-
continue;
|
|
1612
|
-
queued += this.queueRawMessageCount(queue);
|
|
1613
|
-
if (this.processing.has(queueKey) || this.activeBatches.has(queueKey))
|
|
1614
|
-
active = true;
|
|
1615
|
-
if (this.processing.has(queueKey) && !this.activeBatches.has(queueKey))
|
|
1616
|
-
dispatching = true;
|
|
1617
|
-
}
|
|
1618
|
-
for (const queueKey of this.activeBatches.keys()) {
|
|
1619
|
-
if (this.matchesSession(queueKey, sessionKey))
|
|
1620
|
-
active = true;
|
|
1621
|
-
}
|
|
1622
1616
|
const state = {
|
|
1623
1617
|
promise,
|
|
1624
1618
|
releasePromise,
|
|
1625
1619
|
holders: 1,
|
|
1626
|
-
snapshot:
|
|
1627
|
-
prequeue: this.prequeueReservations.get(sessionKey) ?? 0,
|
|
1628
|
-
active,
|
|
1629
|
-
queued,
|
|
1630
|
-
dispatching,
|
|
1631
|
-
cutoff: this.enqueueSequence,
|
|
1632
|
-
},
|
|
1620
|
+
snapshot: this.getSessionSnapshot(sessionKey),
|
|
1633
1621
|
};
|
|
1634
1622
|
this.externalLocks.set(sessionKey, state);
|
|
1635
1623
|
let released = false;
|
|
@@ -1648,6 +1636,50 @@ export class MessageQueue {
|
|
|
1648
1636
|
},
|
|
1649
1637
|
};
|
|
1650
1638
|
}
|
|
1639
|
+
/** Live admission state, including work that arrived after an older gate. */
|
|
1640
|
+
getMessageState(sessionId, messageId, selfAid) {
|
|
1641
|
+
for (const [key, queue] of this.queues) {
|
|
1642
|
+
if (this.matchesSession(key, sessionId) && queue.some(item => this.partsOf(item)
|
|
1643
|
+
.some(part => this.partMatchesMessage(part, messageId, selfAid, key))))
|
|
1644
|
+
return { state: 'queued' };
|
|
1645
|
+
}
|
|
1646
|
+
for (const [key, item] of this.activeBatches) {
|
|
1647
|
+
if (this.matchesSession(key, sessionId) && this.partsOf(item)
|
|
1648
|
+
.some(part => this.partMatchesMessage(part, messageId, selfAid, key))) {
|
|
1649
|
+
return { state: 'active' };
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return { state: 'missing' };
|
|
1653
|
+
}
|
|
1654
|
+
/** Live admission state, including work that arrived after an older gate. */
|
|
1655
|
+
getSessionSnapshot(sessionKey) {
|
|
1656
|
+
let queued = 0;
|
|
1657
|
+
let active = false;
|
|
1658
|
+
let dispatching = false;
|
|
1659
|
+
const activeTaskIds = new Set();
|
|
1660
|
+
for (const [queueKey, queue] of this.queues.entries()) {
|
|
1661
|
+
if (!this.matchesSession(queueKey, sessionKey))
|
|
1662
|
+
continue;
|
|
1663
|
+
queued += this.queueRawMessageCount(queue);
|
|
1664
|
+
if (this.processing.has(queueKey) || this.activeBatches.has(queueKey))
|
|
1665
|
+
active = true;
|
|
1666
|
+
if (this.processing.has(queueKey) && !this.activeBatches.has(queueKey))
|
|
1667
|
+
dispatching = true;
|
|
1668
|
+
}
|
|
1669
|
+
for (const queueKey of this.activeBatches.keys()) {
|
|
1670
|
+
if (!this.matchesSession(queueKey, sessionKey))
|
|
1671
|
+
continue;
|
|
1672
|
+
active = true;
|
|
1673
|
+
const taskId = this.activeStates.get(queueKey)?.taskId;
|
|
1674
|
+
if (taskId)
|
|
1675
|
+
activeTaskIds.add(taskId);
|
|
1676
|
+
}
|
|
1677
|
+
return {
|
|
1678
|
+
prequeue: this.prequeueReservations.get(sessionKey) ?? 0,
|
|
1679
|
+
active, queued, dispatching, cutoff: this.enqueueSequence,
|
|
1680
|
+
activeTaskIds: [...activeTaskIds],
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1651
1683
|
/**
|
|
1652
1684
|
* Mark an admitted message before it enters debounce or other pre-queue
|
|
1653
1685
|
* work. The returned release is idempotent so callers can use it both from
|