evolcore 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +58 -9
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +3 -1
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +393 -108
- package/dist/agents/codex-app-server-client.js +41 -7
- package/dist/agents/codex-runner.js +1292 -220
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +189 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +618 -123
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +98 -123
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +50 -0
- package/dist/cli/trigger-command.js +14 -5
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +19 -5
- package/dist/config/config-manager.js +167 -22
- package/dist/config/contact-book-store.js +25 -3
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +43 -1
- package/dist/core/auth/auth-gateway.js +41 -4
- package/dist/core/auth/authorization-audit.js +216 -8
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +99 -27
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +311 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +288 -80
- package/dist/core/command/menu-protocol.js +1 -1
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +377 -36
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +37 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +33 -3
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +90 -87
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +184 -12
- package/dist/core/message/message-log.js +47 -7
- package/dist/core/message/message-queue.js +227 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +658 -109
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +627 -69
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +27 -14
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +618 -23
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +329 -30
- package/dist/core/session/session-renew.js +37 -13
- package/dist/core/session/session-turn-coordinator.js +16 -5
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +316 -50
- package/dist/ipc.js +459 -29
- package/dist/paths.js +82 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +70 -7
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +13 -3
- package/dist/trigger/scheduler.js +20 -3
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +30 -6
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +3 -3
- package/kits/docs/evolcore/agent-create.md +146 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/group-collaboration.md +251 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +2 -1
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -16,7 +16,7 @@ import { fileCache } from '../daemon-file-cache.js';
|
|
|
16
16
|
import { currentVersion } from '../../config/schema-registry.js';
|
|
17
17
|
import { loadSchema } from '../../config/schema-registry.js';
|
|
18
18
|
import { rolePermissionFields, rolePermissionFieldOptions, rolePermissionFieldPatterns, validateRoleConfig, } from '../../config/role-schema.js';
|
|
19
|
-
import { groupInfo, groupList } from '../../aun/msg/group.js';
|
|
19
|
+
import { groupInfo, groupList, groupDirectoryErrorCode } from '../../aun/msg/group.js';
|
|
20
20
|
import { aidLookup, isValidAid } from '../../aun/aid/identity.js';
|
|
21
21
|
import { isExplicitGroupId } from '../../aun/group-identity.js';
|
|
22
22
|
import { readJsonFile, readLastJsonlLine, scanChatDirs, scanMetaFiles } from '../session/session-fs-store.js';
|
|
@@ -341,7 +341,7 @@ function assertRequest(context, args) {
|
|
|
341
341
|
receivingSelf: context.self,
|
|
342
342
|
});
|
|
343
343
|
}
|
|
344
|
-
if (context.actorRole !== 'owner' && context.actorRole !== 'admin') {
|
|
344
|
+
if (!isDaemonOwnerControl(context) && context.actorRole !== 'owner' && context.actorRole !== 'admin') {
|
|
345
345
|
throw roleError('PERMISSION_DENIED', 'Role management requires Agent owner or admin', context.self, {
|
|
346
346
|
kind: 'role_permission_denied',
|
|
347
347
|
actorRole: context.actorRole,
|
|
@@ -350,7 +350,7 @@ function assertRequest(context, args) {
|
|
|
350
350
|
}
|
|
351
351
|
function assertRolePolicyMutationTarget(context, args) {
|
|
352
352
|
const roleId = requiredRoleId(args, context.self);
|
|
353
|
-
if (context.actorRole === 'admin' && roleId === 'owner') {
|
|
353
|
+
if (!isDaemonOwnerControl(context) && context.actorRole === 'admin' && roleId === 'owner') {
|
|
354
354
|
throw roleError('PERMISSION_DENIED', 'Agent admin cannot modify the owner role policy', context.self, {
|
|
355
355
|
kind: 'role_hierarchy_violation',
|
|
356
356
|
actorRole: context.actorRole,
|
|
@@ -485,7 +485,7 @@ function updateDefaultRoles(context, args, value) {
|
|
|
485
485
|
const registry = readRoleRegistry(self);
|
|
486
486
|
const changesAdminDefault = ['private', 'group'].some(scene => registry.defaultRoles[scene] !== nextDefaults[scene]
|
|
487
487
|
&& (registry.defaultRoles[scene] === 'admin' || nextDefaults[scene] === 'admin'));
|
|
488
|
-
if (context.actorRole !== 'owner' && changesAdminDefault) {
|
|
488
|
+
if (!isDaemonOwnerControl(context) && context.actorRole !== 'owner' && changesAdminDefault) {
|
|
489
489
|
throw roleError('NOT_ALLOWED', 'Only an owner can set or replace an admin default role', self, {
|
|
490
490
|
kind: 'admin_owner_only',
|
|
491
491
|
});
|
|
@@ -521,7 +521,7 @@ async function updateAssignment(context, args, value) {
|
|
|
521
521
|
});
|
|
522
522
|
}
|
|
523
523
|
const current = readRelation(target.peerKey, self);
|
|
524
|
-
if (context.actorRole !== 'owner' && (roleId === 'admin' || current?.config.role === 'admin')) {
|
|
524
|
+
if (!isDaemonOwnerControl(context) && context.actorRole !== 'owner' && (roleId === 'admin' || current?.config.role === 'admin')) {
|
|
525
525
|
throw roleError('NOT_ALLOWED', 'Only an owner can grant or revoke admin', self, {
|
|
526
526
|
kind: 'admin_owner_only',
|
|
527
527
|
target: target.peerKey,
|
|
@@ -545,7 +545,7 @@ async function updateAssignment(context, args, value) {
|
|
|
545
545
|
}
|
|
546
546
|
async function updateAdminAssignment(context, args, value) {
|
|
547
547
|
const self = context.self;
|
|
548
|
-
if (context.actorRole !== 'owner') {
|
|
548
|
+
if (!isDaemonOwnerControl(context) && context.actorRole !== 'owner') {
|
|
549
549
|
throw roleError('NOT_ALLOWED', 'Only an owner can grant or revoke admin', self, { kind: 'admin_owner_only' });
|
|
550
550
|
}
|
|
551
551
|
let target = normalizeTarget(args?.target, self);
|
|
@@ -679,7 +679,7 @@ async function deleteRole(context, args) {
|
|
|
679
679
|
const replacement = hasReplacement
|
|
680
680
|
? validateAssignableRole(args?.replacementRoleId, self, 'replacementRoleId')
|
|
681
681
|
: undefined;
|
|
682
|
-
if (replacement === 'admin' && context.actorRole !== 'owner') {
|
|
682
|
+
if (replacement === 'admin' && !isDaemonOwnerControl(context) && context.actorRole !== 'owner') {
|
|
683
683
|
throw roleError('NOT_ALLOWED', 'Only an owner can replace role assignments with admin', self, {
|
|
684
684
|
kind: 'admin_owner_only',
|
|
685
685
|
});
|
|
@@ -778,13 +778,19 @@ async function targetSnapshot(context, args, policyRevision) {
|
|
|
778
778
|
const self = context.self;
|
|
779
779
|
const target = await validateTargetExists(context, normalizeTarget(args?.target, self), { allowAuthenticatedPrivateActor: true });
|
|
780
780
|
const relation = readRelation(target.peerKey, self);
|
|
781
|
+
const roleView = targetRoleView(self, target, relation?.config ?? null);
|
|
782
|
+
const override = target.kind === 'private'
|
|
783
|
+
? roleView.assignment.explicitRole
|
|
784
|
+
: roleView.assignment.explicitDefaultRole;
|
|
781
785
|
return {
|
|
782
786
|
$schema_version: ROLE_MENU_SCHEMA_VERSION,
|
|
783
787
|
self,
|
|
784
788
|
policyRevision,
|
|
785
789
|
target,
|
|
786
790
|
relationRevision: relation?.revision ?? null,
|
|
787
|
-
|
|
791
|
+
scope: 'relation',
|
|
792
|
+
override,
|
|
793
|
+
...roleView,
|
|
788
794
|
...(target.kind === 'group' ? { requesterRole: requesterRoleResult(context) } : {}),
|
|
789
795
|
relationConfig: relationConfigView(context, target, relation?.config ?? null),
|
|
790
796
|
};
|
|
@@ -926,10 +932,11 @@ async function validateTargetExists(context, target, options = {}) {
|
|
|
926
932
|
}
|
|
927
933
|
return target;
|
|
928
934
|
}
|
|
929
|
-
const info = await directory.getGroup({ from: context.self, groupId: target.groupId, required: ['member'] });
|
|
935
|
+
const info = await directory.getGroup({ from: context.self, groupId: target.groupId, required: ['member', 'state'] });
|
|
930
936
|
if (!info.ok) {
|
|
931
|
-
|
|
932
|
-
|
|
937
|
+
const code = groupDirectoryErrorCode(info.code, info.error);
|
|
938
|
+
throw roleError(code, info.error || 'Unable to verify Group Role target', context.self, {
|
|
939
|
+
kind: code === 'NOT_FOUND' ? 'target_not_found' : 'target_source_unavailable', target,
|
|
933
940
|
});
|
|
934
941
|
}
|
|
935
942
|
const group = info.group;
|
|
@@ -1168,6 +1175,9 @@ function requesterRoleResult(context) {
|
|
|
1168
1175
|
: false,
|
|
1169
1176
|
};
|
|
1170
1177
|
}
|
|
1178
|
+
function isDaemonOwnerControl(context) {
|
|
1179
|
+
return context.isDaemonOwner === true && context.fromControlChannel === true;
|
|
1180
|
+
}
|
|
1171
1181
|
function readAgent(self) {
|
|
1172
1182
|
const agent = read(ConfigTarget.Agent, { self }, { cache: false });
|
|
1173
1183
|
if (!agent)
|
|
@@ -1,7 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps already-collected activity evidence to a command decision. Collection
|
|
3
|
+
* stays at the caller because topic management needs a barrier snapshot while
|
|
4
|
+
* legacy guards intentionally retain their narrower, non-atomic evidence.
|
|
5
|
+
*/
|
|
6
|
+
export function evaluateSessionActivity(evidence) {
|
|
7
|
+
const signals = [
|
|
8
|
+
evidence.active,
|
|
9
|
+
evidence.dispatching,
|
|
10
|
+
evidence.processing,
|
|
11
|
+
evidence.activeTurn,
|
|
12
|
+
evidence.paused,
|
|
13
|
+
evidence.activeStream,
|
|
14
|
+
evidence.pendingPermission,
|
|
15
|
+
evidence.pendingInteraction,
|
|
16
|
+
evidence.activeHandoff,
|
|
17
|
+
evidence.openHandoffTarget,
|
|
18
|
+
];
|
|
19
|
+
if ((evidence.prequeue ?? 0) > 0 || (evidence.queued ?? 0) > 0 || signals.some(signal => signal === true)) {
|
|
20
|
+
return 'busy';
|
|
21
|
+
}
|
|
22
|
+
return signals.some(signal => signal === 'unknown') ? 'unknown' : 'idle';
|
|
23
|
+
}
|
|
24
|
+
/** Collect synchronous probes consistently; a configured probe failure is unknown, not idle. */
|
|
25
|
+
export function probeSessionActivity(evidence, probes) {
|
|
26
|
+
const collected = { ...evidence };
|
|
27
|
+
// Queue/turn evidence is authoritative enough to reject immediately. Avoid
|
|
28
|
+
// touching secondary probes after BUSY is already proven (and preserve the
|
|
29
|
+
// existing paused-thread short-circuit behavior).
|
|
30
|
+
if (evaluateSessionActivity(collected) === 'busy')
|
|
31
|
+
return 'busy';
|
|
32
|
+
for (const key of Object.keys(probes)) {
|
|
33
|
+
const probe = probes[key];
|
|
34
|
+
if (!probe)
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
collected[key] = probe();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
collected[key] = 'unknown';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return evaluateSessionActivity(collected);
|
|
44
|
+
}
|
|
1
45
|
// 支持的命令列表
|
|
2
|
-
const commands = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/stop', '/pause', '/compact', '/repair', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/observable', '/chatmode', '/mentionmode', '/ask', '/resume', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade'];
|
|
46
|
+
const commands = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/stop', '/pause', '/compact', '/renew', '/repair', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/observable', '/chatmode', '/mentionmode', '/ask', '/resume', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade'];
|
|
3
47
|
const deprecatedCommands = ['/clear'];
|
|
4
48
|
const exactBoundaryCommands = new Set(['/pause', '/resume', '/stop']);
|
|
49
|
+
/** Parse the execution command without treating it as a quick/result command. */
|
|
50
|
+
export function parseFullAccessCommand(content) {
|
|
51
|
+
const match = content.match(/^\s*(\/fa|\/fullaccess)(?=\s|$)([\s\S]*)$/);
|
|
52
|
+
if (!match)
|
|
53
|
+
return { matched: false };
|
|
54
|
+
return {
|
|
55
|
+
matched: true,
|
|
56
|
+
command: match[1],
|
|
57
|
+
prompt: match[2].trim(),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
5
60
|
// 命令别名映射
|
|
6
61
|
const aliases = {
|
|
7
62
|
'/s': '/session',
|
|
@@ -10,7 +65,7 @@ const aliases = {
|
|
|
10
65
|
'/base': '/baseagent',
|
|
11
66
|
};
|
|
12
67
|
// 命令快速路径前缀(所有命令都不进入消息队列)
|
|
13
|
-
const quickCommandPrefixes = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/pause', '/clear', '/compact', '/del', '/perm', '/file', '/check', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/observable', '/chatmode', '/mentionmode', '/ask', '/resume', '/base ', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade'];
|
|
68
|
+
const quickCommandPrefixes = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/pause', '/clear', '/compact', '/renew', '/del', '/perm', '/file', '/check', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/observable', '/chatmode', '/mentionmode', '/ask', '/resume', '/base ', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade'];
|
|
14
69
|
/**
|
|
15
70
|
* 计算两个字符串的 Levenshtein 距离(编辑距离)
|
|
16
71
|
*/
|
|
@@ -61,11 +116,13 @@ export function isRecognizedSlashCommand(content) {
|
|
|
61
116
|
export function guardThreadCommand(content, threadId) {
|
|
62
117
|
if (!threadId)
|
|
63
118
|
return undefined;
|
|
64
|
-
const threadBlocked = ['/new', '/slist', '/s', '/session', '/fork', '/del'];
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
119
|
+
const threadBlocked = ['/new', '/slist', '/s', '/session', '/fork', '/del', '/repair'];
|
|
120
|
+
const hasCommandArgument = (command) => new RegExp(`^${command.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:\\s|$)`).test(content);
|
|
121
|
+
const isBlocked = threadBlocked.some(hasCommandArgument);
|
|
122
|
+
const isBaseagentSwitch = /^\/baseagent\s/.test(content);
|
|
123
|
+
const isBaseAliasSwitch = /^\/base\s/.test(content);
|
|
124
|
+
const isRewindMutation = /^\/rewind\s/.test(content);
|
|
125
|
+
if (!isBlocked && !isBaseagentSwitch && !isBaseAliasSwitch && !isRewindMutation)
|
|
69
126
|
return undefined;
|
|
70
127
|
return { kind: 'command.error', text: '⚠️ 话题中不支持此命令' };
|
|
71
128
|
}
|
|
@@ -83,7 +140,7 @@ export function guardRoleCommand(content, activeChatType, isAdmin) {
|
|
|
83
140
|
: [
|
|
84
141
|
...userGroupCommands,
|
|
85
142
|
// 私聊 visitor/member 额外可用:会话自管理 + 私聊专属的 /rewind 历史查看
|
|
86
|
-
'/slist', '/new', '/session', '/rename', '/name', '/del', '/s ', '/rewind',
|
|
143
|
+
'/slist', '/new', '/session', '/rename', '/name', '/del', '/s ', '/rewind', '/renew',
|
|
87
144
|
];
|
|
88
145
|
const isUserCommand = userCommands.some(cmd => content === cmd.trimEnd() || content.startsWith(cmd));
|
|
89
146
|
if (isUserCommand || isAdmin)
|
|
@@ -102,10 +159,16 @@ export async function guardIdleCommand(opts) {
|
|
|
102
159
|
// - 仅带参时需要 idle(无参是列表/用法):/session /baseagent /rewind
|
|
103
160
|
// - /chatmode:在 handler 内部自行做写操作的 idle 检查
|
|
104
161
|
// - /mentionmode:在 handler 内部自行做写操作的 idle 检查
|
|
162
|
+
// /renew owns a stronger gate + authoritative probe and deliberately skips
|
|
163
|
+
// this realtime precheck so delayed retries can return their committed
|
|
164
|
+
// idempotent result even while later work is busy.
|
|
105
165
|
const idleAlways = ['/compact', '/repair', '/fork', '/new'];
|
|
106
166
|
const idleWhenArg = ['/session', '/baseagent', '/rewind'];
|
|
107
|
-
const
|
|
108
|
-
|
|
167
|
+
const usesTopicBackendBoundary = !!opts.threadId
|
|
168
|
+
&& opts.content === '/compact'
|
|
169
|
+
&& typeof opts.messageQueue?.withSessionBarrier === 'function';
|
|
170
|
+
const needsIdle = !usesTopicBackendBoundary && (idleAlways.some(cmd => opts.content === cmd || opts.content.startsWith(cmd + ' ')) ||
|
|
171
|
+
idleWhenArg.some(cmd => opts.content.startsWith(cmd + ' ')));
|
|
109
172
|
if (!needsIdle)
|
|
110
173
|
return undefined;
|
|
111
174
|
if (opts.threadId) {
|
|
@@ -121,10 +184,12 @@ export async function guardIdleCommand(opts) {
|
|
|
121
184
|
catch {
|
|
122
185
|
// Runner mismatch should not block recovery commands such as /baseagent.
|
|
123
186
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
187
|
+
const activity = evaluateSessionActivity({
|
|
188
|
+
queued: opts.messageQueue?.getQueueLength(threadSession.id) ?? 0,
|
|
189
|
+
processing: opts.messageQueue?.isProcessing(threadSession.id) ?? false,
|
|
190
|
+
activeStream: hasActiveStream,
|
|
191
|
+
});
|
|
192
|
+
if (activity === 'busy') {
|
|
128
193
|
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
129
194
|
}
|
|
130
195
|
}
|
|
@@ -132,10 +197,12 @@ export async function guardIdleCommand(opts) {
|
|
|
132
197
|
else if (opts.activeSession) {
|
|
133
198
|
if (opts.isSessionPaused?.(opts.activeSession.id))
|
|
134
199
|
return undefined;
|
|
135
|
-
const
|
|
136
|
-
opts.messageQueue?.
|
|
137
|
-
|
|
138
|
-
|
|
200
|
+
const activity = evaluateSessionActivity({
|
|
201
|
+
queued: opts.messageQueue?.getQueueLength(opts.activeSession.id) ?? 0,
|
|
202
|
+
processing: opts.messageQueue?.isProcessing(opts.activeSession.id) ?? false,
|
|
203
|
+
activeStream: opts.activeAgent?.hasActiveStream(opts.activeSession.id) ?? false,
|
|
204
|
+
});
|
|
205
|
+
if (activity === 'busy') {
|
|
139
206
|
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
140
207
|
}
|
|
141
208
|
}
|