evolcore 0.0.3 → 0.0.4
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 +64 -0
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +1 -31
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +157 -5
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/channels/aun.js +220 -112
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +52 -137
- package/dist/channels/feishu.js +56 -4
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +383 -7
- package/dist/cli/aun-commands.js +11 -11
- package/dist/cli/handoff-command.js +2 -1
- package/dist/cli/init-channel.js +185 -67
- package/dist/cli/init.js +7 -5
- package/dist/cli/trigger-command.js +92 -11
- package/dist/config/access-policy-domain.js +38 -0
- package/dist/config/access-policy.js +134 -0
- package/dist/config/builtin-role-templates.js +27 -14
- package/dist/config/builtin-roles.js +25 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +198 -22
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +173 -5
- package/dist/config/contact-book.js +32 -0
- package/dist/config/contact-operation-service.js +9 -3
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +3 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -2
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +6 -0
- package/dist/core/auth/operation-authorizer.js +8 -0
- package/dist/core/auth/operation-catalog.js +51 -3
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +10 -13
- package/dist/core/command/connect-menu.js +249 -24
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +269 -85
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +158 -31
- package/dist/core/daemon-file-cache.js +28 -0
- package/dist/core/event-catalog.js +29 -0
- package/dist/core/evolagent-registry.js +4 -39
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +18 -14
- package/dist/core/message/inbound-admission.js +124 -0
- package/dist/core/message/message-bridge.js +616 -74
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +185 -10
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +381 -79
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +69 -24
- package/dist/core/protected-paths.js +36 -11
- package/dist/core/session/session-manager.js +25 -3
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +224 -89
- package/dist/ipc.js +81 -4
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/trigger/parser.js +55 -15
- package/dist/trigger/patch.js +4 -1
- package/dist/trigger/scheduler.js +441 -39
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +7 -3
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +4 -4
- package/kits/docs/evolcore/msg.md +5 -5
- package/kits/docs/evolcore/trigger.md +25 -8
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +18 -7
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +374 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +46 -0
- package/kits/templates/roles/member.json +4 -0
- package/kits/templates/roles/visitor.json +4 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +4 -2
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/dist/channels/contact-bind-code.js +0 -134
- package/dist/channels/wecom-card.js +0 -101
- package/dist/channels/wecom-onboarding.js +0 -82
- package/dist/channels/wecom-state.js +0 -191
|
@@ -3,8 +3,11 @@ import { formatPeerKey } from '../relation/peer-identity.js';
|
|
|
3
3
|
import { logger } from '../../utils/logger.js';
|
|
4
4
|
import { isTransientProtocolMessage, messageLogPath } from '../message/message-log.js';
|
|
5
5
|
import { readAllJsonlLines } from './session-fs-store.js';
|
|
6
|
+
import { ResponseModeRegistry } from '../../response-system/registry.js';
|
|
7
|
+
import { registerBuiltinModes } from '../../response-system/modes/index.js';
|
|
8
|
+
import { resolveEffectiveResponseModeConfig } from '../../response-system/config-resolver.js';
|
|
9
|
+
import { normalizeBaseagent } from '../../agents/baseagent.js';
|
|
6
10
|
const DEFAULT_AFTER_HOURS = 24;
|
|
7
|
-
const DEFAULT_EFFORT = 'low';
|
|
8
11
|
const DEFAULT_FALLBACK = 'continue';
|
|
9
12
|
const MAX_CONTEXT_MESSAGES = 40;
|
|
10
13
|
const MAX_CONTEXT_CHARS = 16_000;
|
|
@@ -12,7 +15,7 @@ const MAX_MESSAGE_CHARS = 2_000;
|
|
|
12
15
|
const MODEL_TIMEOUT_MS = 10_000;
|
|
13
16
|
const RECENT_DECISION_TTL_MS = 30_000;
|
|
14
17
|
const NEW_CONFIDENCE_THRESHOLD = 0.85;
|
|
15
|
-
const
|
|
18
|
+
const AUXILIARY_EFFORTS = new Set(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
16
19
|
const EXPLICIT_NEW_RE = /^(?:新话题|换个话题|重新开始|新开会话|新会话)[::,,;;。!!\n]/;
|
|
17
20
|
const EXPLICIT_CONTINUE_RE = /^(?:继续上次|延续上次|继续|接着)[::,,;;。!!\n]/;
|
|
18
21
|
const NON_CONVERSATION_TYPES = new Set([
|
|
@@ -43,41 +46,6 @@ export function selectSessionRenewContext(entries) {
|
|
|
43
46
|
const selected = [entries[0], ...entries.slice(-(MAX_CONTEXT_MESSAGES - 1))];
|
|
44
47
|
return trimContextByChars(selected);
|
|
45
48
|
}
|
|
46
|
-
export function selectLatestHaikuModel(models) {
|
|
47
|
-
let selected;
|
|
48
|
-
for (const rawModel of models) {
|
|
49
|
-
const id = rawModel.trim();
|
|
50
|
-
if (!id)
|
|
51
|
-
continue;
|
|
52
|
-
const leaf = id.toLowerCase().split('/').at(-1) ?? '';
|
|
53
|
-
const tokens = leaf.split('-');
|
|
54
|
-
const haikuIndex = tokens.indexOf('haiku');
|
|
55
|
-
if (haikuIndex < 0)
|
|
56
|
-
continue;
|
|
57
|
-
const versionTokens = haikuIndex <= 1
|
|
58
|
-
? tokens.slice(haikuIndex + 1)
|
|
59
|
-
: tokens.slice(1, haikuIndex);
|
|
60
|
-
const versions = versionTokens
|
|
61
|
-
.filter(token => /^\d{1,2}$/.test(token))
|
|
62
|
-
.map(Number);
|
|
63
|
-
const dates = tokens
|
|
64
|
-
.filter(token => /^\d{8}$/.test(token))
|
|
65
|
-
.map(Number);
|
|
66
|
-
const candidate = {
|
|
67
|
-
id,
|
|
68
|
-
major: versions[0] ?? 0,
|
|
69
|
-
minor: versions[1] ?? 0,
|
|
70
|
-
date: dates.length > 0 ? Math.max(...dates) : 0,
|
|
71
|
-
};
|
|
72
|
-
if (!selected
|
|
73
|
-
|| candidate.major > selected.major
|
|
74
|
-
|| (candidate.major === selected.major && candidate.minor > selected.minor)
|
|
75
|
-
|| (candidate.major === selected.major && candidate.minor === selected.minor && candidate.date > selected.date)) {
|
|
76
|
-
selected = candidate;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return selected?.id;
|
|
80
|
-
}
|
|
81
49
|
function trimContextByChars(entries) {
|
|
82
50
|
if (entries.length === 0)
|
|
83
51
|
return [];
|
|
@@ -100,17 +68,27 @@ export class SessionRenewService {
|
|
|
100
68
|
locks = new Map();
|
|
101
69
|
recentDecisions = new Map();
|
|
102
70
|
now;
|
|
103
|
-
|
|
71
|
+
effectiveConfigResolver;
|
|
72
|
+
eventBus;
|
|
73
|
+
responseModeRegistry = new ResponseModeRegistry();
|
|
104
74
|
constructor(sessionManager, inferenceProviderResolver, options = {}) {
|
|
105
75
|
this.sessionManager = sessionManager;
|
|
106
76
|
this.inferenceProviderResolver = inferenceProviderResolver;
|
|
107
77
|
this.now = options.now ?? (() => Date.now());
|
|
108
|
-
this.
|
|
78
|
+
this.effectiveConfigResolver = options.resolveEffectiveConfig;
|
|
79
|
+
this.eventBus = options.eventBus;
|
|
80
|
+
registerBuiltinModes(this.responseModeRegistry);
|
|
109
81
|
}
|
|
110
82
|
async resolve(request) {
|
|
111
|
-
const
|
|
83
|
+
const effectiveConfig = this.resolveEffectiveConfig(request);
|
|
84
|
+
const config = effectiveConfig?.session_renew;
|
|
112
85
|
if (!config || config.enabled !== true)
|
|
113
86
|
return { session: request.session, renewed: false };
|
|
87
|
+
// Thread / processing sessions are already an explicit routing decision.
|
|
88
|
+
// Do not let a cached or active main session replace them before resolveLocked().
|
|
89
|
+
if (request.session.threadId || request.session.processingState) {
|
|
90
|
+
return { session: request.session, renewed: false };
|
|
91
|
+
}
|
|
114
92
|
const routeKey = this.routeKey(request);
|
|
115
93
|
return await this.withLock(routeKey, async () => {
|
|
116
94
|
const cached = this.recentDecisions.get(routeKey);
|
|
@@ -133,12 +111,11 @@ export class SessionRenewService {
|
|
|
133
111
|
active.identity = request.session.identity;
|
|
134
112
|
return { session: active, renewed: true, decision: 'new', source: 'fallback' };
|
|
135
113
|
}
|
|
136
|
-
return await this.resolveLocked(routeKey, request, config);
|
|
114
|
+
return await this.resolveLocked(routeKey, request, config, effectiveConfig);
|
|
137
115
|
});
|
|
138
116
|
}
|
|
139
|
-
async resolveLocked(routeKey, request, config) {
|
|
140
|
-
|
|
141
|
-
return { session: request.session, renewed: false };
|
|
117
|
+
async resolveLocked(routeKey, request, config, effectiveConfig) {
|
|
118
|
+
const evaluationStartedAt = Date.now();
|
|
142
119
|
const afterHours = validPositiveNumber(config.after_hours) ?? DEFAULT_AFTER_HOURS;
|
|
143
120
|
const entries = this.readConversationEntries(request.session);
|
|
144
121
|
const last = entries[entries.length - 1];
|
|
@@ -146,7 +123,7 @@ export class SessionRenewService {
|
|
|
146
123
|
if (request.isNewSession)
|
|
147
124
|
return { session: request.session, renewed: false };
|
|
148
125
|
const idleMs = this.now() - request.session.updatedAt;
|
|
149
|
-
return await this.finishDecision(routeKey, request, 'new', 'missing_history', idleMs);
|
|
126
|
+
return await this.finishDecision(routeKey, request, 'new', 'missing_history', idleMs, evaluationStartedAt);
|
|
150
127
|
}
|
|
151
128
|
const idleMs = this.now() - last.ts;
|
|
152
129
|
if (idleMs <= afterHours * 60 * 60 * 1000)
|
|
@@ -162,13 +139,22 @@ export class SessionRenewService {
|
|
|
162
139
|
source = 'reply';
|
|
163
140
|
}
|
|
164
141
|
else {
|
|
165
|
-
|
|
142
|
+
const auxiliary = this.resolveAuxiliaryConfig(effectiveConfig, request.session.baseagent);
|
|
143
|
+
let judgeMetadata = {
|
|
144
|
+
responseMode: auxiliary.responseMode,
|
|
145
|
+
baseagent: request.session.baseagent,
|
|
146
|
+
model: auxiliary.model,
|
|
147
|
+
effort: auxiliary.effort,
|
|
148
|
+
};
|
|
166
149
|
try {
|
|
167
|
-
|
|
150
|
+
if (!auxiliary.model)
|
|
151
|
+
throw new SessionRenewJudgeError('auxiliary_model_missing');
|
|
152
|
+
if (auxiliary.invalidEffort)
|
|
153
|
+
throw new SessionRenewJudgeError('auxiliary_effort_invalid');
|
|
154
|
+
const provider = this.inferenceProviderResolver.getTextInferenceProvider(request.channelName, request.session.baseagent, request.selfAid);
|
|
168
155
|
if (!provider)
|
|
169
|
-
throw new
|
|
170
|
-
const judged = await this.judgeWithModel(request,
|
|
171
|
-
judgeModel = judged.model;
|
|
156
|
+
throw new SessionRenewJudgeError('provider_unavailable');
|
|
157
|
+
const judged = await this.judgeWithModel(request, entries, idleMs, provider, auxiliary);
|
|
172
158
|
decision = judged.decision === 'new' && judged.confidence < NEW_CONFIDENCE_THRESHOLD
|
|
173
159
|
? 'continue'
|
|
174
160
|
: judged.decision;
|
|
@@ -177,15 +163,19 @@ export class SessionRenewService {
|
|
|
177
163
|
catch (error) {
|
|
178
164
|
decision = config.fallback_action ?? DEFAULT_FALLBACK;
|
|
179
165
|
source = 'fallback';
|
|
166
|
+
judgeMetadata = { ...judgeMetadata, fallbackReason: sessionRenewFallbackReason(error) };
|
|
180
167
|
logger.warn(`[SessionRenew] judge failed, using fallback=${decision}: ${error instanceof Error ? error.message : String(error)}`);
|
|
181
168
|
}
|
|
182
|
-
return await this.finishDecision(routeKey, request, decision, source, idleMs,
|
|
169
|
+
return await this.finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt, judgeMetadata);
|
|
183
170
|
}
|
|
184
|
-
return await this.finishDecision(routeKey, request, decision, source, idleMs);
|
|
171
|
+
return await this.finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt);
|
|
185
172
|
}
|
|
186
|
-
async finishDecision(routeKey, request, decision, source, idleMs,
|
|
173
|
+
async finishDecision(routeKey, request, decision, source, idleMs, evaluationStartedAt, judge) {
|
|
187
174
|
const selected = decision === 'new'
|
|
188
|
-
? await this.sessionManager.createRenewedSession(request.session
|
|
175
|
+
? await this.sessionManager.createRenewedSession(request.session, {
|
|
176
|
+
source,
|
|
177
|
+
causation: request.causation,
|
|
178
|
+
})
|
|
189
179
|
: request.session;
|
|
190
180
|
selected.identity = request.session.identity;
|
|
191
181
|
this.recentDecisions.set(routeKey, {
|
|
@@ -195,12 +185,39 @@ export class SessionRenewService {
|
|
|
195
185
|
source,
|
|
196
186
|
expiresAt: this.now() + RECENT_DECISION_TTL_MS,
|
|
197
187
|
});
|
|
198
|
-
logger.info(`[SessionRenew] session=${request.session.id} selected=${selected.id} decision=${decision} source=${source}
|
|
188
|
+
logger.info(`[SessionRenew] session=${request.session.id} selected=${selected.id} decision=${decision} source=${source}`
|
|
189
|
+
+ ` idleHours=${(idleMs / 3_600_000).toFixed(2)}`
|
|
190
|
+
+ ` responseMode=${judge?.responseMode ?? 'none'}`
|
|
191
|
+
+ ` baseagent=${judge?.baseagent ?? 'none'}`
|
|
192
|
+
+ ` model=${judge?.model ?? 'none'}`
|
|
193
|
+
+ ` effort=${judge?.effort ?? 'provider-default'}`
|
|
194
|
+
+ ` fallbackReason=${judge?.fallbackReason ?? 'none'}`);
|
|
195
|
+
this.eventBus?.publish({
|
|
196
|
+
type: 'session:renew-evaluated',
|
|
197
|
+
candidateSessionId: request.session.id,
|
|
198
|
+
selectedSessionId: selected.id,
|
|
199
|
+
renewed: decision === 'new',
|
|
200
|
+
decision,
|
|
201
|
+
source,
|
|
202
|
+
channel: request.session.channel,
|
|
203
|
+
channelName: request.channelName,
|
|
204
|
+
channelId: request.channelId,
|
|
205
|
+
selfAid: request.selfAid,
|
|
206
|
+
idleHours: Number((idleMs / 3_600_000).toFixed(2)),
|
|
207
|
+
responseMode: judge?.responseMode,
|
|
208
|
+
baseagent: judge?.baseagent,
|
|
209
|
+
model: judge?.model,
|
|
210
|
+
effort: judge?.effort,
|
|
211
|
+
fallbackReason: judge?.fallbackReason,
|
|
212
|
+
durationMs: Math.max(0, Date.now() - evaluationStartedAt),
|
|
213
|
+
timestamp: this.now(),
|
|
214
|
+
causation: request.causation,
|
|
215
|
+
});
|
|
199
216
|
return { session: selected, renewed: decision === 'new', decision, source };
|
|
200
217
|
}
|
|
201
|
-
|
|
202
|
-
if (this.
|
|
203
|
-
return this.
|
|
218
|
+
resolveEffectiveConfig(request) {
|
|
219
|
+
if (this.effectiveConfigResolver)
|
|
220
|
+
return this.effectiveConfigResolver(request);
|
|
204
221
|
if (!request.selfAid)
|
|
205
222
|
return undefined;
|
|
206
223
|
const peerKeyId = request.chatType === 'group'
|
|
@@ -210,7 +227,30 @@ export class SessionRenewService {
|
|
|
210
227
|
return resolveEffective({
|
|
211
228
|
self: request.selfAid,
|
|
212
229
|
peerKey,
|
|
213
|
-
}, { cache: true })
|
|
230
|
+
}, { cache: true, expand: true });
|
|
231
|
+
}
|
|
232
|
+
resolveAuxiliaryConfig(config, baseagent) {
|
|
233
|
+
const resolved = resolveEffectiveResponseModeConfig(this.responseModeRegistry, config);
|
|
234
|
+
const rawModel = resolved.modeConfig.auxiliaryModel;
|
|
235
|
+
const canonicalBaseagent = normalizeBaseagent(baseagent).canonical;
|
|
236
|
+
const selectedModel = typeof rawModel === 'string'
|
|
237
|
+
? rawModel
|
|
238
|
+
: rawModel && typeof rawModel === 'object' && !Array.isArray(rawModel)
|
|
239
|
+
? rawModel[canonicalBaseagent]
|
|
240
|
+
: undefined;
|
|
241
|
+
const model = typeof selectedModel === 'string' && selectedModel.trim()
|
|
242
|
+
? selectedModel.trim()
|
|
243
|
+
: undefined;
|
|
244
|
+
const rawEffort = resolved.modeConfig.auxiliaryEffort;
|
|
245
|
+
const effort = typeof rawEffort === 'string' && AUXILIARY_EFFORTS.has(rawEffort)
|
|
246
|
+
? rawEffort
|
|
247
|
+
: undefined;
|
|
248
|
+
return {
|
|
249
|
+
responseMode: resolved.mode.id,
|
|
250
|
+
model,
|
|
251
|
+
effort,
|
|
252
|
+
invalidEffort: rawEffort !== undefined && effort === undefined,
|
|
253
|
+
};
|
|
214
254
|
}
|
|
215
255
|
readConversationEntries(session) {
|
|
216
256
|
const entries = readAllJsonlLines(messageLogPath(this.sessionManager.getChatDir(session)));
|
|
@@ -218,7 +258,7 @@ export class SessionRenewService {
|
|
|
218
258
|
.filter(entry => isSessionRenewConversationEntry(entry, session.id))
|
|
219
259
|
.sort((left, right) => left.ts - right.ts);
|
|
220
260
|
}
|
|
221
|
-
async judgeWithModel(request,
|
|
261
|
+
async judgeWithModel(request, entries, idleMs, provider, auxiliary) {
|
|
222
262
|
const context = selectSessionRenewContext(entries).map(entry => ({
|
|
223
263
|
dir: entry.dir,
|
|
224
264
|
content: entry.content.slice(0, MAX_MESSAGE_CHARS),
|
|
@@ -243,20 +283,14 @@ export class SessionRenewService {
|
|
|
243
283
|
let timer;
|
|
244
284
|
try {
|
|
245
285
|
const operation = (async () => {
|
|
246
|
-
if (!provider.listModels)
|
|
247
|
-
throw new Error('Claude text inference provider does not expose a model list');
|
|
248
|
-
const models = await provider.listModels(controller.signal);
|
|
249
|
-
const model = selectLatestHaikuModel(models);
|
|
250
|
-
if (!model)
|
|
251
|
-
throw new Error('Claude model list contains no Haiku model');
|
|
252
286
|
const text = await provider.completeText({
|
|
253
|
-
model,
|
|
254
|
-
effort:
|
|
287
|
+
model: auxiliary.model,
|
|
288
|
+
...(auxiliary.effort ? { effort: auxiliary.effort } : {}),
|
|
255
289
|
system: systemPrompt,
|
|
256
290
|
input: prompt,
|
|
257
291
|
signal: controller.signal,
|
|
258
292
|
});
|
|
259
|
-
return { model, text };
|
|
293
|
+
return { model: auxiliary.model, text };
|
|
260
294
|
})();
|
|
261
295
|
const timeout = new Promise((_resolve, reject) => {
|
|
262
296
|
timer = setTimeout(() => {
|
|
@@ -292,6 +326,28 @@ export class SessionRenewService {
|
|
|
292
326
|
}
|
|
293
327
|
}
|
|
294
328
|
}
|
|
329
|
+
class SessionRenewJudgeError extends Error {
|
|
330
|
+
code;
|
|
331
|
+
constructor(code) {
|
|
332
|
+
super(code);
|
|
333
|
+
this.code = code;
|
|
334
|
+
this.name = 'SessionRenewJudgeError';
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function sessionRenewFallbackReason(error) {
|
|
338
|
+
if (error instanceof SessionRenewJudgeError)
|
|
339
|
+
return error.code;
|
|
340
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
341
|
+
if (/timed out|abort/i.test(message))
|
|
342
|
+
return 'timeout';
|
|
343
|
+
if (/HTTP \d+/i.test(message))
|
|
344
|
+
return 'http_error';
|
|
345
|
+
if (/no JSON|invalid decision|invalid confidence/i.test(message))
|
|
346
|
+
return 'invalid_response';
|
|
347
|
+
if (/no text|empty/i.test(message))
|
|
348
|
+
return 'empty_response';
|
|
349
|
+
return 'inference_error';
|
|
350
|
+
}
|
|
295
351
|
function validPositiveNumber(value) {
|
|
296
352
|
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
297
353
|
}
|
|
@@ -101,7 +101,11 @@ export class SessionTurnCoordinator {
|
|
|
101
101
|
turnId: active.turnId,
|
|
102
102
|
taskId: active.taskId,
|
|
103
103
|
generation: active.generation,
|
|
104
|
-
status: reason === '
|
|
104
|
+
status: reason === 'daemon_restart'
|
|
105
|
+
? 'interrupted'
|
|
106
|
+
: reason === 'stop' || reason === 'explicit_cancel'
|
|
107
|
+
? 'cancelled'
|
|
108
|
+
: 'superseded',
|
|
105
109
|
reason,
|
|
106
110
|
finishedAt: Date.now(),
|
|
107
111
|
},
|
package/dist/eck/kit-renderer.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { isEckSnapshotsEnabled } from '../config-store.js';
|
|
4
4
|
import { eckDebugDir } from '../paths.js';
|
|
5
5
|
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { clearRoleStoreCache } from '../config/role-store.js';
|
|
6
7
|
import { loadManifest, loadManifestMeta, invalidateManifestCache, evaluateWhen, renderTemplate, renderLoopSection, resolvePathWithDiag, loadSectionFiles, loadChildTemplate, buildPathMappings, shortenPath, } from './manifest-engine.js';
|
|
7
8
|
const DEFAULT_MANIFEST_FILE = 'eck_manifest.json';
|
|
8
9
|
// ── Caches ──
|
|
@@ -19,9 +20,18 @@ export function loadKitManifest() {
|
|
|
19
20
|
const sections = loadManifest(DEFAULT_MANIFEST_FILE);
|
|
20
21
|
logger.info(`[KitRenderer] Loaded manifest: ${sections.length} sections`);
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* kits 统一失效入口:`kits/` 下的任何东西改了,都从这里一次清干净。
|
|
25
|
+
*
|
|
26
|
+
* 必须连带清 role-store 快照:它的签名含角色模板 mtime,而模板内容走 FileCache
|
|
27
|
+
* 的 on-reload(平时不查盘)。若"模板改盘 → 失效前发生一次鉴权读取",快照就会以
|
|
28
|
+
* **新 mtime + 旧内容** 落盘;此后签名不再变化,光清 FileCache 也换不掉它——
|
|
29
|
+
* 授权数据会永久停在旧版本。
|
|
30
|
+
*/
|
|
22
31
|
export function invalidateKitCache() {
|
|
23
32
|
invalidateManifestCache();
|
|
24
33
|
_sessionPathCache.clear();
|
|
34
|
+
clearRoleStoreCache();
|
|
25
35
|
}
|
|
26
36
|
export function invalidateSessionCache(sessionId) {
|
|
27
37
|
_sessionPathCache.delete(sessionId);
|
|
@@ -203,7 +213,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
203
213
|
sameEgressIp: '对端与本端共享同一出口 IP',
|
|
204
214
|
groupId: '群组 ID(群聊时)',
|
|
205
215
|
groupRulesPath: '群规则本地物化文件(relations/<peerKey>/rules.md)',
|
|
206
|
-
groupRulesStatus: '
|
|
216
|
+
groupRulesStatus: '群规则状态(ignored/synced/cached/missing/forbidden/invalid_metadata/file_mismatch/too_large/unreadable/error)',
|
|
207
217
|
groupRulesError: '群规则同步错误',
|
|
208
218
|
chatType: '聊天类型(private=私聊 / group=群聊 / null=本地开发)',
|
|
209
219
|
channel: '渠道类型(aun/feishu/wechat/dingtalk/qqbot/wecom)',
|
|
@@ -226,6 +236,7 @@ const PARAM_DESCRIPTIONS = {
|
|
|
226
236
|
osInfo: '操作系统及版本(如 Windows 11 Pro (win32 10.0.26200))',
|
|
227
237
|
threadId: '话题 ID(多话题路由时)',
|
|
228
238
|
chatMode: '会话模式(interactive=同步交互 / proactive=主动推送)',
|
|
239
|
+
isTriggerTask: '当前任务是否来源于 Trigger(true/false)',
|
|
229
240
|
proactivePreTool1stMsgChk: 'proactive 前置工具首消息检查(true/false)',
|
|
230
241
|
proactiveToolUseReminder: 'proactive 工具调用提醒(true/false)',
|
|
231
242
|
proactiveFirstSendRequired: '当前消息是否要求首次非发送工具前先发送表态',
|
|
@@ -51,6 +51,59 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
|
|
|
51
51
|
const isOwnerHint = item.kind === 'owner-hint';
|
|
52
52
|
const isHandoff = item.kind === 'handoff';
|
|
53
53
|
const handoffPreviousSentinel = `\x00ECMSG-HANDOFF-PREV-${randomUUID()}\x00`;
|
|
54
|
+
const requiredReplacements = (item.handoff?.requiredEntries ?? []).map(entry => ({
|
|
55
|
+
entry,
|
|
56
|
+
sentinel: `\x00ECMSG-HANDOFF-REQ-${randomUUID()}\x00`,
|
|
57
|
+
}));
|
|
58
|
+
const contextReplacements = (item.handoff?.contextEntries ?? []).map(entry => ({
|
|
59
|
+
entry,
|
|
60
|
+
sentinel: `\x00ECMSG-HANDOFF-CTX-${randomUUID()}\x00`,
|
|
61
|
+
}));
|
|
62
|
+
const originReplacements = (item.handoff?.originEntries ?? []).map(entry => ({
|
|
63
|
+
entry,
|
|
64
|
+
previousSentinel: `\x00ECMSG-HANDOFF-ORIGIN-PREV-${randomUUID()}\x00`,
|
|
65
|
+
responseSentinel: `\x00ECMSG-HANDOFF-ORIGIN-RESP-${randomUUID()}\x00`,
|
|
66
|
+
}));
|
|
67
|
+
const fallbackRequiredEntries = item.handoff?.returnPolicy === 'required'
|
|
68
|
+
&& item.handoff.handoffId
|
|
69
|
+
&& item.handoff.previousContent !== undefined
|
|
70
|
+
&& requiredReplacements.length === 0
|
|
71
|
+
? [{
|
|
72
|
+
entry: {
|
|
73
|
+
handoffId: item.handoff.handoffId,
|
|
74
|
+
previousContent: item.handoff.previousContent,
|
|
75
|
+
previousMessageId: item.handoff.previousMessageId,
|
|
76
|
+
},
|
|
77
|
+
sentinel: `\x00ECMSG-HANDOFF-REQ-${randomUUID()}\x00`,
|
|
78
|
+
}]
|
|
79
|
+
: [];
|
|
80
|
+
const fallbackContextEntries = item.handoff?.returnPolicy === 'none'
|
|
81
|
+
&& item.handoff.previousContent !== undefined
|
|
82
|
+
&& contextReplacements.length === 0
|
|
83
|
+
? [{
|
|
84
|
+
entry: {
|
|
85
|
+
previousContent: item.handoff.previousContent,
|
|
86
|
+
previousMessageId: item.handoff.previousMessageId,
|
|
87
|
+
},
|
|
88
|
+
sentinel: `\x00ECMSG-HANDOFF-CTX-${randomUUID()}\x00`,
|
|
89
|
+
}]
|
|
90
|
+
: [];
|
|
91
|
+
const allRequiredReplacements = [...requiredReplacements, ...fallbackRequiredEntries];
|
|
92
|
+
const allContextReplacements = [...contextReplacements, ...fallbackContextEntries];
|
|
93
|
+
const fallbackOriginEntries = item.handoff?.kind === 'response_to_origin'
|
|
94
|
+
&& item.handoff.previousContent !== undefined
|
|
95
|
+
&& originReplacements.length === 0
|
|
96
|
+
? [{
|
|
97
|
+
entry: {
|
|
98
|
+
previousContent: item.handoff.previousContent,
|
|
99
|
+
responseContent: item.content,
|
|
100
|
+
previousMessageId: item.handoff.previousMessageId,
|
|
101
|
+
},
|
|
102
|
+
previousSentinel: handoffPreviousSentinel,
|
|
103
|
+
responseSentinel: contentSentinel,
|
|
104
|
+
}]
|
|
105
|
+
: [];
|
|
106
|
+
const allOriginReplacements = [...originReplacements, ...fallbackOriginEntries];
|
|
54
107
|
// item-level vars: session vars overlaid with this message's own sender/timestamp.
|
|
55
108
|
const itemVars = {
|
|
56
109
|
...sessionVars,
|
|
@@ -91,6 +144,20 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
|
|
|
91
144
|
handoffPreviousMessageId: item.handoff?.previousMessageId ?? undefined,
|
|
92
145
|
handoffPreviousContent: item.handoff ? handoffPreviousSentinel : undefined,
|
|
93
146
|
handoffReturnPolicy: item.handoff?.returnPolicy,
|
|
147
|
+
handoffRequiredEntries: allRequiredReplacements.map(({ entry, sentinel }) => ({
|
|
148
|
+
handoffId: entry.handoffId,
|
|
149
|
+
previousContent: sentinel,
|
|
150
|
+
previousMessageId: entry.previousMessageId ?? undefined,
|
|
151
|
+
})),
|
|
152
|
+
handoffContextEntries: allContextReplacements.map(({ entry, sentinel }) => ({
|
|
153
|
+
previousContent: sentinel,
|
|
154
|
+
previousMessageId: entry.previousMessageId ?? undefined,
|
|
155
|
+
})),
|
|
156
|
+
handoffOriginEntries: allOriginReplacements.map(({ entry, previousSentinel, responseSentinel }) => ({
|
|
157
|
+
previousContent: previousSentinel,
|
|
158
|
+
responseContent: responseSentinel,
|
|
159
|
+
previousMessageId: entry.previousMessageId ?? undefined,
|
|
160
|
+
})),
|
|
94
161
|
// content held as a per-call random sentinel, swapped back post-render.
|
|
95
162
|
// Using a UUID means no real message can collide with it.
|
|
96
163
|
content: contentSentinel,
|
|
@@ -109,9 +176,20 @@ function renderOneItem(item, sessionVars, sessionCache, contentSentinel) {
|
|
|
109
176
|
? renderTemplate(rawContent, itemVars, /* stripBlankLines */ false)
|
|
110
177
|
: rawContent;
|
|
111
178
|
// swap the sentinel back to the real message text (literal replace, no parsing).
|
|
112
|
-
|
|
179
|
+
let withContent = rendered
|
|
113
180
|
.split(contentSentinel).join(item.content)
|
|
114
181
|
.split(handoffPreviousSentinel).join(item.handoff?.previousContent ?? '');
|
|
182
|
+
for (const { entry, sentinel } of allRequiredReplacements) {
|
|
183
|
+
withContent = withContent.split(sentinel).join(entry.previousContent);
|
|
184
|
+
}
|
|
185
|
+
for (const { entry, sentinel } of allContextReplacements) {
|
|
186
|
+
withContent = withContent.split(sentinel).join(entry.previousContent);
|
|
187
|
+
}
|
|
188
|
+
for (const { entry, previousSentinel, responseSentinel } of allOriginReplacements) {
|
|
189
|
+
withContent = withContent
|
|
190
|
+
.split(previousSentinel).join(entry.previousContent)
|
|
191
|
+
.split(responseSentinel).join(entry.responseContent);
|
|
192
|
+
}
|
|
115
193
|
if (withContent.trim())
|
|
116
194
|
out.push(withContent.replace(/\s+$/, ''));
|
|
117
195
|
}
|