@elizaos/agent 2.0.0-alpha.155 → 2.0.0-alpha.161
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/package.json +5 -5
- package/packages/agent/src/api/plugin-routes.d.ts.map +1 -1
- package/packages/agent/src/api/plugin-routes.js +50 -0
- package/packages/agent/src/config/types.eliza.d.ts +2 -0
- package/packages/agent/src/config/types.eliza.d.ts.map +1 -1
- package/packages/typescript/src/features/autonomy/execution-facade.d.ts.map +1 -1
- package/packages/typescript/src/features/autonomy/execution-facade.js +27 -9
- package/packages/typescript/src/optimization/ab-analysis.d.ts +3 -0
- package/packages/typescript/src/optimization/ab-analysis.d.ts.map +1 -0
- package/packages/typescript/src/optimization/ab-analysis.js +8 -0
- package/packages/typescript/src/optimization-root-dir.d.ts +3 -0
- package/packages/typescript/src/optimization-root-dir.d.ts.map +1 -0
- package/packages/typescript/src/optimization-root-dir.js +9 -0
- package/packages/typescript/src/plugin.d.ts.map +1 -1
- package/packages/typescript/src/plugin.js +10 -3
- package/packages/typescript/src/runtime.d.ts +41 -3
- package/packages/typescript/src/runtime.d.ts.map +1 -1
- package/packages/typescript/src/runtime.js +700 -30
- package/packages/typescript/src/services/message.d.ts +1 -0
- package/packages/typescript/src/services/message.d.ts.map +1 -1
- package/packages/typescript/src/services/message.js +328 -222
- package/packages/typescript/src/streaming-context.d.ts +9 -0
- package/packages/typescript/src/streaming-context.d.ts.map +1 -1
- package/packages/typescript/src/streaming-context.js +45 -0
- package/packages/typescript/src/trajectory-context.d.ts +6 -0
- package/packages/typescript/src/trajectory-context.d.ts.map +1 -1
- package/packages/typescript/src/types/events.d.ts +18 -1
- package/packages/typescript/src/types/events.d.ts.map +1 -1
- package/packages/typescript/src/types/events.js +2 -0
- package/packages/typescript/src/types/index.d.ts +4 -0
- package/packages/typescript/src/types/index.d.ts.map +1 -1
- package/packages/typescript/src/types/index.js +4 -0
- package/packages/typescript/src/types/pipeline-hooks.d.ts +234 -0
- package/packages/typescript/src/types/pipeline-hooks.d.ts.map +1 -0
- package/packages/typescript/src/types/pipeline-hooks.js +111 -0
- package/packages/typescript/src/types/prompt-optimization-hooks.d.ts +41 -0
- package/packages/typescript/src/types/prompt-optimization-hooks.d.ts.map +1 -0
- package/packages/typescript/src/types/prompt-optimization-hooks.js +1 -0
- package/packages/typescript/src/types/prompt-optimization-score-card.d.ts +22 -0
- package/packages/typescript/src/types/prompt-optimization-score-card.d.ts.map +1 -0
- package/packages/typescript/src/types/prompt-optimization-score-card.js +72 -0
- package/packages/typescript/src/types/prompt-optimization-trace.d.ts +53 -0
- package/packages/typescript/src/types/prompt-optimization-trace.d.ts.map +1 -0
- package/packages/typescript/src/types/prompt-optimization-trace.js +19 -0
- package/packages/typescript/src/types/runtime.d.ts +40 -1
- package/packages/typescript/src/types/runtime.d.ts.map +1 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { v4 } from "uuid";
|
|
2
2
|
import { parseActionParams } from "../actions";
|
|
3
|
-
import { createUniqueUuid } from "../entities";
|
|
4
3
|
import { formatTaskCompletionStatus, getTaskCompletionCacheKey, } from "../features/advanced-capabilities/evaluators/task-completion";
|
|
4
|
+
import { createUniqueUuid } from "../entities";
|
|
5
5
|
import { logger } from "../logger";
|
|
6
6
|
import { imageDescriptionTemplate, messageHandlerTemplate, multiStepDecisionTemplate, multiStepSummaryTemplate, postActionDecisionTemplate, shouldRespondTemplate, } from "../prompts";
|
|
7
7
|
import { isExplicitSelfModificationRequest } from "../should-respond";
|
|
8
|
-
import { runWithStreamingContext } from "../streaming-context";
|
|
8
|
+
import { getModelStreamChunkDeliveryDepth, runWithStreamingContext, } from "../streaming-context";
|
|
9
9
|
import { runWithTrajectoryContext, setTrajectoryPurpose, } from "../trajectory-context";
|
|
10
10
|
import { EventType } from "../types/events";
|
|
11
11
|
import { ModelType } from "../types/model";
|
|
12
|
+
import { incomingPipelineHookContext, modelStreamChunkPipelineHookContext, outgoingPipelineHookContext, parallelWithShouldRespondPipelineHookContext, preShouldRespondPipelineHookContext, } from "../types/pipeline-hooks";
|
|
12
13
|
import { asUUID, ChannelType, ContentType } from "../types/primitives";
|
|
13
14
|
import { composePromptFromState, getLocalServerUrl, parseBooleanFromText, parseKeyValueXml, truncateToCompleteSentence, } from "../utils";
|
|
14
15
|
import { AVAILABLE_CONTEXTS_STATE_KEY, attachAvailableContexts, CONTEXT_ROUTING_STATE_KEY, mergeContextRouting, parseContextRoutingMetadata, setContextRoutingMetadata, } from "../utils/context-routing";
|
|
@@ -775,11 +776,18 @@ export class DefaultMessageService {
|
|
|
775
776
|
: undefined;
|
|
776
777
|
}
|
|
777
778
|
return await runWithTrajectoryContext(typeof trajectoryStepId === "string" && trajectoryStepId.trim() !== ""
|
|
778
|
-
? {
|
|
779
|
+
? {
|
|
780
|
+
trajectoryStepId: trajectoryStepId.trim(),
|
|
781
|
+
runId: runtime.getCurrentRunId?.(),
|
|
782
|
+
roomId: message.roomId,
|
|
783
|
+
messageId: message.id,
|
|
784
|
+
}
|
|
779
785
|
: undefined, async () => {
|
|
780
786
|
// Determine shouldRespondModel from options or runtime settings
|
|
781
787
|
const shouldRespondModelSetting = runtime.getSetting("SHOULD_RESPOND_MODEL");
|
|
782
788
|
const resolvedShouldRespondModel = normalizeShouldRespondModelType(options?.shouldRespondModel ?? shouldRespondModelSetting);
|
|
789
|
+
// Single ID used for tracking, streaming, and the final message (before opts / chunk wrapper).
|
|
790
|
+
const responseId = asUUID(v4());
|
|
783
791
|
// WHY voice detection wraps onStreamChunk here instead of using a
|
|
784
792
|
// separate ResponseStreamExtractor + AsyncLocalStorage context:
|
|
785
793
|
//
|
|
@@ -815,6 +823,19 @@ export class DefaultMessageService {
|
|
|
815
823
|
streamTextFallback += chunk;
|
|
816
824
|
streamText = streamTextFallback;
|
|
817
825
|
}
|
|
826
|
+
// Skip when this callback is invoked from `useModel`'s stream loop:
|
|
827
|
+
// `source: "use_model"` already ran for the same raw chunk (Node ALS).
|
|
828
|
+
if (getModelStreamChunkDeliveryDepth() === 0) {
|
|
829
|
+
await runtime.applyPipelineHooks("model_stream_chunk", modelStreamChunkPipelineHookContext({
|
|
830
|
+
source: "message_service",
|
|
831
|
+
chunk,
|
|
832
|
+
messageId,
|
|
833
|
+
roomId: message.roomId,
|
|
834
|
+
runId: runtime.getCurrentRunId(),
|
|
835
|
+
responseId,
|
|
836
|
+
accumulated,
|
|
837
|
+
}));
|
|
838
|
+
}
|
|
818
839
|
// Only run first-sentence TTS detection when `accumulated` is present.
|
|
819
840
|
// Raw-token streams (no accumulated) may contain XML markup or partial
|
|
820
841
|
// structured output that would garble hasFirstSentence() and TTS.
|
|
@@ -956,8 +977,6 @@ export class DefaultMessageService {
|
|
|
956
977
|
: undefined;
|
|
957
978
|
// Set up timeout monitoring
|
|
958
979
|
let timeoutId;
|
|
959
|
-
// Single ID used for tracking, streaming, and the final message
|
|
960
|
-
const responseId = asUUID(v4());
|
|
961
980
|
try {
|
|
962
981
|
runtime.logger.info({
|
|
963
982
|
src: "service:message",
|
|
@@ -1266,12 +1285,10 @@ export class DefaultMessageService {
|
|
|
1266
1285
|
mode: "none",
|
|
1267
1286
|
};
|
|
1268
1287
|
}
|
|
1269
|
-
//
|
|
1270
|
-
|
|
1271
|
-
state = attachAvailableContexts(state, runtime);
|
|
1272
|
-
// Get room and mention context
|
|
1288
|
+
// Room context for shouldRespond (fetch before compose so providers see
|
|
1289
|
+
// post-attachment and post-incoming-hook message state).
|
|
1273
1290
|
const room = await runtime.getRoom(message.roomId);
|
|
1274
|
-
// Process attachments before
|
|
1291
|
+
// Process attachments before state composition / incoming hooks
|
|
1275
1292
|
if (message.content.attachments && message.content.attachments.length > 0) {
|
|
1276
1293
|
message.content.attachments = await this.processAttachments(runtime, message.content.attachments);
|
|
1277
1294
|
if (message.id) {
|
|
@@ -1284,169 +1301,94 @@ export class DefaultMessageService {
|
|
|
1284
1301
|
});
|
|
1285
1302
|
}
|
|
1286
1303
|
}
|
|
1304
|
+
const preIncomingHookText = typeof message.content?.text === "string" ? message.content.text : "";
|
|
1305
|
+
await runtime.applyPipelineHooks("incoming_before_compose", incomingPipelineHookContext(message, {
|
|
1306
|
+
roomId: message.roomId,
|
|
1307
|
+
responseId,
|
|
1308
|
+
runId,
|
|
1309
|
+
}));
|
|
1310
|
+
const postIncomingHookText = typeof message.content?.text === "string" ? message.content.text : "";
|
|
1311
|
+
if (message.id && postIncomingHookText !== preIncomingHookText) {
|
|
1312
|
+
await runtime.updateMemory({
|
|
1313
|
+
id: message.id,
|
|
1314
|
+
content: message.content,
|
|
1315
|
+
});
|
|
1316
|
+
await runtime.queueEmbeddingGeneration({ ...message, id: message.id }, "normal");
|
|
1317
|
+
}
|
|
1287
1318
|
const promptAttachments = resolvePromptAttachments(message.content.attachments);
|
|
1288
|
-
|
|
1289
|
-
let
|
|
1290
|
-
|
|
1291
|
-
let dualPressureLog = null;
|
|
1292
|
-
let shouldRespondClassifierAction = null;
|
|
1319
|
+
// Compose initial state (after incoming hooks so providers/actions text matches this turn)
|
|
1320
|
+
let state = await runtime.composeState(message, ["ENTITIES", "CHARACTER", "RECENT_MESSAGES", "ACTIONS"], true, false);
|
|
1321
|
+
state = attachAvailableContexts(state, runtime);
|
|
1293
1322
|
const metadata = typeof message.content.metadata === "object" &&
|
|
1294
1323
|
message.content.metadata !== null
|
|
1295
1324
|
? message.content.metadata
|
|
1296
1325
|
: null;
|
|
1297
1326
|
const isAutonomous = metadata?.isAutonomous === true;
|
|
1298
1327
|
const autonomyMode = typeof metadata?.autonomyMode === "string" ? metadata.autonomyMode : null;
|
|
1328
|
+
await runtime.applyPipelineHooks("pre_should_respond", preShouldRespondPipelineHookContext(message, {
|
|
1329
|
+
roomId: message.roomId,
|
|
1330
|
+
responseId,
|
|
1331
|
+
runId,
|
|
1332
|
+
state,
|
|
1333
|
+
isAutonomous,
|
|
1334
|
+
}));
|
|
1335
|
+
let shouldRespondToMessage = true;
|
|
1336
|
+
let terminalDecision = null;
|
|
1337
|
+
let routedDecision = null;
|
|
1338
|
+
let dualPressureLog = null;
|
|
1339
|
+
let shouldRespondClassifierAction = null;
|
|
1340
|
+
const parallelJoin = {};
|
|
1341
|
+
const setTranslatedUserText = (text) => {
|
|
1342
|
+
parallelJoin.translatedUserText = text;
|
|
1343
|
+
};
|
|
1344
|
+
const parallelHookCtx = parallelWithShouldRespondPipelineHookContext({
|
|
1345
|
+
roomId: message.roomId,
|
|
1346
|
+
responseId,
|
|
1347
|
+
runId,
|
|
1348
|
+
message,
|
|
1349
|
+
state,
|
|
1350
|
+
room: room ?? undefined,
|
|
1351
|
+
mentionContext,
|
|
1352
|
+
isAutonomous,
|
|
1353
|
+
setTranslatedUserText,
|
|
1354
|
+
});
|
|
1299
1355
|
if (isAutonomous) {
|
|
1300
1356
|
runtime.logger.debug({ src: "service:message", autonomyMode }, "Autonomy message bypassing shouldRespond checks");
|
|
1301
1357
|
shouldRespondToMessage = true;
|
|
1358
|
+
await runtime.applyPipelineHooks("parallel_with_should_respond", parallelHookCtx);
|
|
1302
1359
|
}
|
|
1303
1360
|
else {
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
state = {
|
|
1327
|
-
...state,
|
|
1328
|
-
values: {
|
|
1329
|
-
...state.values,
|
|
1330
|
-
dualPressureThreshold: resolveDualPressureThreshold(runtime),
|
|
1331
|
-
},
|
|
1332
|
-
};
|
|
1333
|
-
const shouldRespondState = prepareShouldRespondState(state);
|
|
1334
|
-
// Need LLM evaluation for ambiguous case
|
|
1335
|
-
const _shouldRespondPrompt = composePromptFromState({
|
|
1336
|
-
state: shouldRespondState,
|
|
1337
|
-
template: runtime.character.templates?.shouldRespondTemplate ||
|
|
1338
|
-
shouldRespondTemplate,
|
|
1339
|
-
});
|
|
1340
|
-
runtime.logger.debug({
|
|
1341
|
-
src: "service:message",
|
|
1342
|
-
agentName: runtime.character.name ?? "Agent",
|
|
1343
|
-
reason: responseDecision.reason,
|
|
1344
|
-
model: opts.shouldRespondModel,
|
|
1345
|
-
}, "Using LLM evaluation");
|
|
1346
|
-
// Use dynamicPromptExecFromState for structured output with validation
|
|
1347
|
-
setTrajectoryPurpose("should_respond");
|
|
1348
|
-
const responseObject = await runtime.dynamicPromptExecFromState({
|
|
1349
|
-
state: shouldRespondState,
|
|
1350
|
-
params: {
|
|
1351
|
-
prompt: runtime.character.templates?.shouldRespondTemplate ||
|
|
1352
|
-
shouldRespondTemplate,
|
|
1353
|
-
...(promptAttachments ? { attachments: promptAttachments } : {}),
|
|
1354
|
-
},
|
|
1355
|
-
schema: [
|
|
1356
|
-
// Decision schema - no streaming, no per-field validation needed
|
|
1357
|
-
// WHY: This is internal decision-making, not user-facing output
|
|
1358
|
-
{
|
|
1359
|
-
field: "name",
|
|
1360
|
-
description: "The name of the agent responding",
|
|
1361
|
-
validateField: false,
|
|
1362
|
-
streamField: false,
|
|
1363
|
-
},
|
|
1364
|
-
{
|
|
1365
|
-
field: "reasoning",
|
|
1366
|
-
description: "Your reasoning for this decision",
|
|
1367
|
-
validateField: false,
|
|
1368
|
-
streamField: false,
|
|
1369
|
-
},
|
|
1370
|
-
{
|
|
1371
|
-
field: "speak_up",
|
|
1372
|
-
description: "Integer 0-100 pressure TO engage",
|
|
1373
|
-
validateField: false,
|
|
1374
|
-
streamField: false,
|
|
1375
|
-
},
|
|
1376
|
-
{
|
|
1377
|
-
field: "hold_back",
|
|
1378
|
-
description: "Integer 0-100 pressure to STAY QUIET",
|
|
1379
|
-
validateField: false,
|
|
1380
|
-
streamField: false,
|
|
1381
|
-
},
|
|
1382
|
-
{
|
|
1383
|
-
field: "action",
|
|
1384
|
-
description: "REPLY | RESPOND | IGNORE | STOP (REPLY and RESPOND both mean engage)",
|
|
1385
|
-
validateField: false,
|
|
1386
|
-
streamField: false,
|
|
1387
|
-
},
|
|
1388
|
-
{
|
|
1389
|
-
field: "primaryContext",
|
|
1390
|
-
description: "Primary domain context from available_contexts (e.g., wallet, knowledge)",
|
|
1391
|
-
validateField: false,
|
|
1392
|
-
streamField: false,
|
|
1393
|
-
},
|
|
1394
|
-
{
|
|
1395
|
-
field: "secondaryContexts",
|
|
1396
|
-
description: "Optional comma-separated additional domain contexts",
|
|
1397
|
-
validateField: false,
|
|
1398
|
-
streamField: false,
|
|
1399
|
-
},
|
|
1400
|
-
{
|
|
1401
|
-
field: "evidenceTurnIds",
|
|
1402
|
-
description: "Optional comma-separated message IDs that influenced this decision",
|
|
1403
|
-
validateField: false,
|
|
1404
|
-
streamField: false,
|
|
1405
|
-
},
|
|
1406
|
-
],
|
|
1407
|
-
options: {
|
|
1408
|
-
contextCheckLevel: 0, // Set to 0 for now
|
|
1409
|
-
// Classifier failures are usually transient provider hiccups;
|
|
1410
|
-
// give this internal decision one short retry window.
|
|
1411
|
-
maxRetries: Math.max(1, Math.min(opts.maxRetries, 2)),
|
|
1412
|
-
retryBackoff: {
|
|
1413
|
-
initialMs: 500,
|
|
1414
|
-
multiplier: 2,
|
|
1415
|
-
maxMs: 2000,
|
|
1416
|
-
},
|
|
1417
|
-
modelType: resolveShouldRespondModelType(opts.shouldRespondModel),
|
|
1418
|
-
preferredEncapsulation: "toon",
|
|
1419
|
-
},
|
|
1361
|
+
const [classifyOutcome] = await Promise.all([
|
|
1362
|
+
this.runNonAutonomousShouldRespondClassify(runtime, message, state, room ?? undefined, mentionContext, opts, promptAttachments),
|
|
1363
|
+
runtime.applyPipelineHooks("parallel_with_should_respond", parallelHookCtx),
|
|
1364
|
+
]);
|
|
1365
|
+
shouldRespondToMessage = classifyOutcome.shouldRespondToMessage;
|
|
1366
|
+
terminalDecision = classifyOutcome.terminalDecision;
|
|
1367
|
+
routedDecision = classifyOutcome.routedDecision;
|
|
1368
|
+
dualPressureLog = classifyOutcome.dualPressureLog;
|
|
1369
|
+
shouldRespondClassifierAction =
|
|
1370
|
+
classifyOutcome.shouldRespondClassifierAction;
|
|
1371
|
+
state = classifyOutcome.state;
|
|
1372
|
+
}
|
|
1373
|
+
const joinedTranslation = typeof parallelJoin.translatedUserText === "string"
|
|
1374
|
+
? parallelJoin.translatedUserText
|
|
1375
|
+
: undefined;
|
|
1376
|
+
if (joinedTranslation !== undefined &&
|
|
1377
|
+
joinedTranslation !== message.content.text) {
|
|
1378
|
+
message.content.text = joinedTranslation;
|
|
1379
|
+
if (message.id) {
|
|
1380
|
+
await runtime.updateMemory({
|
|
1381
|
+
id: message.id,
|
|
1382
|
+
content: message.content,
|
|
1420
1383
|
});
|
|
1421
|
-
runtime.
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
const hasValidClassifierAction = actionUpper.length > 0 && ALLOWED_CLASSIFIER_ACTIONS.has(actionUpper);
|
|
1427
|
-
routedDecision = parseContextRoutingMetadata(responseObject);
|
|
1428
|
-
setContextRoutingMetadata(message, routedDecision);
|
|
1429
|
-
if (!hasValidClassifierAction) {
|
|
1430
|
-
runtime.logger.warn({
|
|
1431
|
-
src: "service:message",
|
|
1432
|
-
action: responseObject?.action,
|
|
1433
|
-
}, "Classifier response missing valid action; treating as IGNORE");
|
|
1434
|
-
terminalDecision = "IGNORE";
|
|
1435
|
-
shouldRespondToMessage = false;
|
|
1436
|
-
}
|
|
1437
|
-
else {
|
|
1438
|
-
const dual = applyDualPressureToClassifierAction(runtime, responseObject, rawAction);
|
|
1439
|
-
dualPressureLog = dual.pressure;
|
|
1440
|
-
shouldRespondClassifierAction = dual.finalActionUpper;
|
|
1441
|
-
if (dual.finalActionUpper === "IGNORE" ||
|
|
1442
|
-
dual.finalActionUpper === "STOP") {
|
|
1443
|
-
terminalDecision = dual.finalActionUpper;
|
|
1444
|
-
}
|
|
1445
|
-
shouldRespondToMessage =
|
|
1446
|
-
dual.finalActionUpper === "REPLY" ||
|
|
1447
|
-
dual.finalActionUpper === "RESPOND";
|
|
1448
|
-
}
|
|
1384
|
+
await runtime.queueEmbeddingGeneration({ ...message, id: message.id }, "normal");
|
|
1385
|
+
}
|
|
1386
|
+
if (message.id) {
|
|
1387
|
+
runtime.stateCache.delete(message.id);
|
|
1388
|
+
runtime.stateCache.delete(`${message.id}_action_results`);
|
|
1449
1389
|
}
|
|
1390
|
+
state = await runtime.composeState(message, ["ENTITIES", "CHARACTER", "RECENT_MESSAGES", "ACTIONS"], true, false);
|
|
1391
|
+
state = attachAvailableContexts(state, runtime);
|
|
1450
1392
|
}
|
|
1451
1393
|
let responseContent = null;
|
|
1452
1394
|
let responseMessages = [];
|
|
@@ -1501,8 +1443,8 @@ export class DefaultMessageService {
|
|
|
1501
1443
|
if (responseContent?.providers && responseContent.providers.length > 0) {
|
|
1502
1444
|
state = await runtime.composeState(message, responseContent.providers, false, false);
|
|
1503
1445
|
}
|
|
1504
|
-
// Save response memory to database
|
|
1505
|
-
if (responseMessages.length > 0) {
|
|
1446
|
+
// Save response memory to database (simple mode persists after hooks; see below)
|
|
1447
|
+
if (responseMessages.length > 0 && mode !== "simple") {
|
|
1506
1448
|
for (const responseMemory of responseMessages) {
|
|
1507
1449
|
// Update the content in case inReplyTo was added
|
|
1508
1450
|
if (responseContent) {
|
|
@@ -1511,11 +1453,6 @@ export class DefaultMessageService {
|
|
|
1511
1453
|
runtime.logger.debug({ src: "service:message", memoryId: responseMemory.id }, "Saving response to memory");
|
|
1512
1454
|
await runtime.createMemory(responseMemory, "messages");
|
|
1513
1455
|
await this.emitMessageSent(runtime, responseMemory, message.content.source ?? "messageHandler");
|
|
1514
|
-
// Track IDs for simple-mode so we can delete orphaned
|
|
1515
|
-
// memories if reflection overrides the deferred emit.
|
|
1516
|
-
if (mode === "simple" && responseMemory.id) {
|
|
1517
|
-
pendingSimpleMemoryIds.push(responseMemory.id);
|
|
1518
|
-
}
|
|
1519
1456
|
}
|
|
1520
1457
|
}
|
|
1521
1458
|
if (responseContent) {
|
|
@@ -1528,11 +1465,25 @@ export class DefaultMessageService {
|
|
|
1528
1465
|
providers: responseContent.providers,
|
|
1529
1466
|
}, "Simple response used providers");
|
|
1530
1467
|
}
|
|
1531
|
-
//
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1468
|
+
// WHY order: hooks → createMemory → deferred callback matches wire + DB.
|
|
1469
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(responseContent, {
|
|
1470
|
+
source: "simple",
|
|
1471
|
+
roomId: message.roomId,
|
|
1472
|
+
message,
|
|
1473
|
+
responseId: responseContent.responseId ?? responseMessages[0]?.id,
|
|
1474
|
+
}));
|
|
1475
|
+
if (responseMessages.length > 0) {
|
|
1476
|
+
for (const responseMemory of responseMessages) {
|
|
1477
|
+
if (responseContent) {
|
|
1478
|
+
responseMemory.content = responseContent;
|
|
1479
|
+
}
|
|
1480
|
+
runtime.logger.debug({ src: "service:message", memoryId: responseMemory.id }, "Saving response to memory");
|
|
1481
|
+
await runtime.createMemory(responseMemory, "messages");
|
|
1482
|
+
await this.emitMessageSent(runtime, responseMemory, message.content.source ?? "messageHandler");
|
|
1483
|
+
if (responseMemory.id) {
|
|
1484
|
+
pendingSimpleMemoryIds.push(responseMemory.id);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1536
1487
|
}
|
|
1537
1488
|
pendingSimpleEmit = responseContent;
|
|
1538
1489
|
}
|
|
@@ -1609,11 +1560,11 @@ export class DefaultMessageService {
|
|
|
1609
1560
|
simple: true,
|
|
1610
1561
|
inReplyTo: createUniqueUuid(runtime, message.id),
|
|
1611
1562
|
};
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1563
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(terminalContent, {
|
|
1564
|
+
source: "excluded",
|
|
1565
|
+
roomId: message.roomId,
|
|
1566
|
+
message,
|
|
1567
|
+
}));
|
|
1617
1568
|
const terminalMemory = {
|
|
1618
1569
|
id: asUUID(v4()),
|
|
1619
1570
|
entityId: runtime.agentId,
|
|
@@ -1625,6 +1576,9 @@ export class DefaultMessageService {
|
|
|
1625
1576
|
await runtime.createMemory(terminalMemory, "messages");
|
|
1626
1577
|
await this.emitMessageSent(runtime, terminalMemory, message.content.source ?? "messageHandler");
|
|
1627
1578
|
runtime.logger.debug({ src: "service:message", memoryId: terminalMemory.id }, "Saved terminal response to memory");
|
|
1579
|
+
if (callback) {
|
|
1580
|
+
await callback(terminalContent);
|
|
1581
|
+
}
|
|
1628
1582
|
}
|
|
1629
1583
|
// Clean up the response ID
|
|
1630
1584
|
clearLatestResponseId(runtime.agentId, message.roomId, responseId);
|
|
@@ -1636,9 +1590,12 @@ export class DefaultMessageService {
|
|
|
1636
1590
|
responseContent.evalCallbacks = content;
|
|
1637
1591
|
}
|
|
1638
1592
|
if (callback) {
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1593
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(content, {
|
|
1594
|
+
source: "evaluate",
|
|
1595
|
+
roomId: message.roomId,
|
|
1596
|
+
message,
|
|
1597
|
+
responseId: content.responseId,
|
|
1598
|
+
}));
|
|
1642
1599
|
return callback(content);
|
|
1643
1600
|
}
|
|
1644
1601
|
return [];
|
|
@@ -1794,6 +1751,156 @@ export class DefaultMessageService {
|
|
|
1794
1751
|
: {}),
|
|
1795
1752
|
};
|
|
1796
1753
|
}
|
|
1754
|
+
async runNonAutonomousShouldRespondClassify(runtime, message, state, room, mentionContext, opts, promptAttachments) {
|
|
1755
|
+
let shouldRespondToMessage = true;
|
|
1756
|
+
let terminalDecision = null;
|
|
1757
|
+
let routedDecision = null;
|
|
1758
|
+
let dualPressureLog = null;
|
|
1759
|
+
let shouldRespondClassifierAction = null;
|
|
1760
|
+
let workingState = state;
|
|
1761
|
+
const checkShouldRespondEnabled = runtime.isCheckShouldRespondEnabled();
|
|
1762
|
+
const responseDecision = this.shouldRespond(runtime, message, room, mentionContext);
|
|
1763
|
+
runtime.logger.debug({ src: "service:message", responseDecision, checkShouldRespondEnabled }, "Response decision");
|
|
1764
|
+
if (!checkShouldRespondEnabled) {
|
|
1765
|
+
runtime.logger.debug({ src: "service:message" }, "checkShouldRespond disabled, always responding (ChatGPT mode)");
|
|
1766
|
+
shouldRespondToMessage = true;
|
|
1767
|
+
}
|
|
1768
|
+
else if (responseDecision.skipEvaluation) {
|
|
1769
|
+
runtime.logger.debug({
|
|
1770
|
+
src: "service:message",
|
|
1771
|
+
agentName: runtime.character.name ?? "Agent",
|
|
1772
|
+
reason: responseDecision.reason,
|
|
1773
|
+
}, "Skipping LLM evaluation");
|
|
1774
|
+
routedDecision = parseContextRoutingMetadata(responseDecision);
|
|
1775
|
+
setContextRoutingMetadata(message, routedDecision);
|
|
1776
|
+
shouldRespondToMessage = responseDecision.shouldRespond;
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
workingState = {
|
|
1780
|
+
...workingState,
|
|
1781
|
+
values: {
|
|
1782
|
+
...workingState.values,
|
|
1783
|
+
dualPressureThreshold: resolveDualPressureThreshold(runtime),
|
|
1784
|
+
},
|
|
1785
|
+
};
|
|
1786
|
+
const shouldRespondState = prepareShouldRespondState(workingState);
|
|
1787
|
+
const _shouldRespondPrompt = composePromptFromState({
|
|
1788
|
+
state: shouldRespondState,
|
|
1789
|
+
template: runtime.character.templates?.shouldRespondTemplate ||
|
|
1790
|
+
shouldRespondTemplate,
|
|
1791
|
+
});
|
|
1792
|
+
runtime.logger.debug({
|
|
1793
|
+
src: "service:message",
|
|
1794
|
+
agentName: runtime.character.name ?? "Agent",
|
|
1795
|
+
reason: responseDecision.reason,
|
|
1796
|
+
model: opts.shouldRespondModel,
|
|
1797
|
+
}, "Using LLM evaluation");
|
|
1798
|
+
setTrajectoryPurpose("should_respond");
|
|
1799
|
+
const responseObject = await runtime.dynamicPromptExecFromState({
|
|
1800
|
+
state: shouldRespondState,
|
|
1801
|
+
params: {
|
|
1802
|
+
prompt: runtime.character.templates?.shouldRespondTemplate ||
|
|
1803
|
+
shouldRespondTemplate,
|
|
1804
|
+
...(promptAttachments ? { attachments: promptAttachments } : {}),
|
|
1805
|
+
},
|
|
1806
|
+
schema: [
|
|
1807
|
+
{
|
|
1808
|
+
field: "name",
|
|
1809
|
+
description: "The name of the agent responding",
|
|
1810
|
+
validateField: false,
|
|
1811
|
+
streamField: false,
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
field: "reasoning",
|
|
1815
|
+
description: "Your reasoning for this decision",
|
|
1816
|
+
validateField: false,
|
|
1817
|
+
streamField: false,
|
|
1818
|
+
},
|
|
1819
|
+
{
|
|
1820
|
+
field: "speak_up",
|
|
1821
|
+
description: "Integer 0-100 pressure TO engage",
|
|
1822
|
+
validateField: false,
|
|
1823
|
+
streamField: false,
|
|
1824
|
+
},
|
|
1825
|
+
{
|
|
1826
|
+
field: "hold_back",
|
|
1827
|
+
description: "Integer 0-100 pressure to STAY QUIET",
|
|
1828
|
+
validateField: false,
|
|
1829
|
+
streamField: false,
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
field: "action",
|
|
1833
|
+
description: "REPLY | RESPOND | IGNORE | STOP (REPLY and RESPOND both mean engage)",
|
|
1834
|
+
validateField: false,
|
|
1835
|
+
streamField: false,
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
field: "primaryContext",
|
|
1839
|
+
description: "Primary domain context from available_contexts (e.g., wallet, knowledge)",
|
|
1840
|
+
validateField: false,
|
|
1841
|
+
streamField: false,
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
field: "secondaryContexts",
|
|
1845
|
+
description: "Optional comma-separated additional domain contexts",
|
|
1846
|
+
validateField: false,
|
|
1847
|
+
streamField: false,
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
field: "evidenceTurnIds",
|
|
1851
|
+
description: "Optional comma-separated message IDs that influenced this decision",
|
|
1852
|
+
validateField: false,
|
|
1853
|
+
streamField: false,
|
|
1854
|
+
},
|
|
1855
|
+
],
|
|
1856
|
+
options: {
|
|
1857
|
+
contextCheckLevel: 0,
|
|
1858
|
+
maxRetries: Math.max(1, Math.min(opts.maxRetries, 2)),
|
|
1859
|
+
retryBackoff: {
|
|
1860
|
+
initialMs: 500,
|
|
1861
|
+
multiplier: 2,
|
|
1862
|
+
maxMs: 2000,
|
|
1863
|
+
},
|
|
1864
|
+
modelType: resolveShouldRespondModelType(opts.shouldRespondModel),
|
|
1865
|
+
preferredEncapsulation: "toon",
|
|
1866
|
+
},
|
|
1867
|
+
});
|
|
1868
|
+
runtime.logger.debug({ src: "service:message", responseObject }, "Parsed evaluation result");
|
|
1869
|
+
const rawAction = typeof responseObject?.action === "string" ? responseObject.action : "";
|
|
1870
|
+
const actionUpper = rawAction.trim().toUpperCase();
|
|
1871
|
+
const hasValidClassifierAction = actionUpper.length > 0 && ALLOWED_CLASSIFIER_ACTIONS.has(actionUpper);
|
|
1872
|
+
routedDecision = parseContextRoutingMetadata(responseObject);
|
|
1873
|
+
setContextRoutingMetadata(message, routedDecision);
|
|
1874
|
+
if (!hasValidClassifierAction) {
|
|
1875
|
+
runtime.logger.warn({
|
|
1876
|
+
src: "service:message",
|
|
1877
|
+
action: responseObject?.action,
|
|
1878
|
+
}, "Classifier response missing valid action; treating as IGNORE");
|
|
1879
|
+
terminalDecision = "IGNORE";
|
|
1880
|
+
shouldRespondToMessage = false;
|
|
1881
|
+
}
|
|
1882
|
+
else {
|
|
1883
|
+
const dual = applyDualPressureToClassifierAction(runtime, responseObject, rawAction);
|
|
1884
|
+
dualPressureLog = dual.pressure;
|
|
1885
|
+
shouldRespondClassifierAction = dual.finalActionUpper;
|
|
1886
|
+
if (dual.finalActionUpper === "IGNORE" ||
|
|
1887
|
+
dual.finalActionUpper === "STOP") {
|
|
1888
|
+
terminalDecision = dual.finalActionUpper;
|
|
1889
|
+
}
|
|
1890
|
+
shouldRespondToMessage =
|
|
1891
|
+
dual.finalActionUpper === "REPLY" ||
|
|
1892
|
+
dual.finalActionUpper === "RESPOND";
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
return {
|
|
1896
|
+
shouldRespondToMessage,
|
|
1897
|
+
terminalDecision,
|
|
1898
|
+
routedDecision,
|
|
1899
|
+
dualPressureLog,
|
|
1900
|
+
shouldRespondClassifierAction,
|
|
1901
|
+
state: workingState,
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1797
1904
|
/**
|
|
1798
1905
|
* Determines whether the agent should respond to a message.
|
|
1799
1906
|
* Uses simple rules for obvious cases (DM, mentions) and defers to LLM for ambiguous cases.
|
|
@@ -2135,7 +2242,8 @@ export class DefaultMessageService {
|
|
|
2135
2242
|
accumulatedState = withActionResults(continuation.state, traceActionResults);
|
|
2136
2243
|
}
|
|
2137
2244
|
accumulatedState = withTaskCompletion(accumulatedState, taskCompletion);
|
|
2138
|
-
if (continuation.responseMessages.length > 0
|
|
2245
|
+
if (continuation.responseMessages.length > 0 &&
|
|
2246
|
+
continuation.mode !== "simple") {
|
|
2139
2247
|
for (const responseMemory of continuation.responseMessages) {
|
|
2140
2248
|
responseMemory.content = responseContent;
|
|
2141
2249
|
await runtime.createMemory(responseMemory, "messages");
|
|
@@ -2144,10 +2252,22 @@ export class DefaultMessageService {
|
|
|
2144
2252
|
responseMessages.push(...continuation.responseMessages);
|
|
2145
2253
|
}
|
|
2146
2254
|
if (continuation.mode === "simple") {
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2255
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(responseContent, {
|
|
2256
|
+
source: "continuation_simple",
|
|
2257
|
+
roomId: message.roomId,
|
|
2258
|
+
message,
|
|
2259
|
+
responseId: responseContent.responseId ??
|
|
2260
|
+
continuation.responseMessages[0]?.id,
|
|
2261
|
+
}));
|
|
2262
|
+
if (continuation.responseMessages.length > 0) {
|
|
2263
|
+
for (const responseMemory of continuation.responseMessages) {
|
|
2264
|
+
responseMemory.content = responseContent;
|
|
2265
|
+
await runtime.createMemory(responseMemory, "messages");
|
|
2266
|
+
await this.emitMessageSent(runtime, responseMemory, message.content.source ?? "messageHandler");
|
|
2150
2267
|
}
|
|
2268
|
+
responseMessages.push(...continuation.responseMessages);
|
|
2269
|
+
}
|
|
2270
|
+
if (callback) {
|
|
2151
2271
|
await callback(responseContent);
|
|
2152
2272
|
}
|
|
2153
2273
|
break;
|
|
@@ -2218,7 +2338,8 @@ export class DefaultMessageService {
|
|
|
2218
2338
|
else {
|
|
2219
2339
|
accumulatedState = withTaskCompletion(withActionResults(continuation.state, initialActionResults), taskCompletion);
|
|
2220
2340
|
}
|
|
2221
|
-
if (continuation.responseMessages.length > 0
|
|
2341
|
+
if (continuation.responseMessages.length > 0 &&
|
|
2342
|
+
continuation.mode !== "simple") {
|
|
2222
2343
|
for (const responseMemory of continuation.responseMessages) {
|
|
2223
2344
|
responseMemory.content = responseContent;
|
|
2224
2345
|
await runtime.createMemory(responseMemory, "messages");
|
|
@@ -2227,10 +2348,21 @@ export class DefaultMessageService {
|
|
|
2227
2348
|
responseMessages.push(...continuation.responseMessages);
|
|
2228
2349
|
}
|
|
2229
2350
|
if (continuation.mode === "simple") {
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2351
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(responseContent, {
|
|
2352
|
+
source: "continuation_simple",
|
|
2353
|
+
roomId: message.roomId,
|
|
2354
|
+
message,
|
|
2355
|
+
responseId: responseContent.responseId ?? continuation.responseMessages[0]?.id,
|
|
2356
|
+
}));
|
|
2357
|
+
if (continuation.responseMessages.length > 0) {
|
|
2358
|
+
for (const responseMemory of continuation.responseMessages) {
|
|
2359
|
+
responseMemory.content = responseContent;
|
|
2360
|
+
await runtime.createMemory(responseMemory, "messages");
|
|
2361
|
+
await this.emitMessageSent(runtime, responseMemory, message.content.source ?? "messageHandler");
|
|
2233
2362
|
}
|
|
2363
|
+
responseMessages.push(...continuation.responseMessages);
|
|
2364
|
+
}
|
|
2365
|
+
if (callback) {
|
|
2234
2366
|
await callback(responseContent);
|
|
2235
2367
|
}
|
|
2236
2368
|
return {
|
|
@@ -2285,38 +2417,6 @@ export class DefaultMessageService {
|
|
|
2285
2417
|
if (!state.values?.actionNames) {
|
|
2286
2418
|
runtime.logger.warn({ src: "service:message" }, "actionNames data missing from state");
|
|
2287
2419
|
}
|
|
2288
|
-
// Self-modification requests already have a deterministic action path.
|
|
2289
|
-
// Skip the planner so group-chat style updates reliably hit MODIFY_CHARACTER.
|
|
2290
|
-
if (isExplicitSelfModificationRequest(message.content.text || "")) {
|
|
2291
|
-
const modifyCharacterAction = runtime.actions.find((action) => action.name === "MODIFY_CHARACTER");
|
|
2292
|
-
const canHandleSelfModification = await modifyCharacterAction?.validate?.(runtime, message, state);
|
|
2293
|
-
if (canHandleSelfModification) {
|
|
2294
|
-
const responseContent = {
|
|
2295
|
-
thought: "Directly route explicit self-modification request to MODIFY_CHARACTER.",
|
|
2296
|
-
actions: ["MODIFY_CHARACTER"],
|
|
2297
|
-
providers: [],
|
|
2298
|
-
text: "",
|
|
2299
|
-
simple: false,
|
|
2300
|
-
responseId,
|
|
2301
|
-
};
|
|
2302
|
-
const responseMessages = [
|
|
2303
|
-
{
|
|
2304
|
-
id: responseId,
|
|
2305
|
-
entityId: runtime.agentId,
|
|
2306
|
-
agentId: runtime.agentId,
|
|
2307
|
-
content: responseContent,
|
|
2308
|
-
roomId: message.roomId,
|
|
2309
|
-
createdAt: Date.now(),
|
|
2310
|
-
},
|
|
2311
|
-
];
|
|
2312
|
-
return {
|
|
2313
|
-
responseContent,
|
|
2314
|
-
responseMessages,
|
|
2315
|
-
state,
|
|
2316
|
-
mode: "actions",
|
|
2317
|
-
};
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
2420
|
let responseContent = null;
|
|
2321
2421
|
// Create streaming context for retry state tracking
|
|
2322
2422
|
const streamingExtractor = opts.onStreamChunk
|
|
@@ -2701,7 +2801,7 @@ Output ONLY the continuation, starting immediately after the last character abov
|
|
|
2701
2801
|
.filter(Boolean)
|
|
2702
2802
|
.join(" ");
|
|
2703
2803
|
}
|
|
2704
|
-
replyText =
|
|
2804
|
+
replyText = truncateToCompleteSentence(replyText.trim(), 2000);
|
|
2705
2805
|
const responseContent = {
|
|
2706
2806
|
thought: `Explain the structured-output failure during ${stage}.`,
|
|
2707
2807
|
actions: ["REPLY"],
|
|
@@ -2915,11 +3015,17 @@ Output ONLY the continuation, starting immediately after the last character abov
|
|
|
2915
3015
|
completedProviders: Array.from(completedProviders),
|
|
2916
3016
|
}, `Providers took too long (>${PROVIDERS_TOTAL_TIMEOUT_MS}ms) - slow providers: ${pendingProviders.join(", ")}`);
|
|
2917
3017
|
if (callback) {
|
|
2918
|
-
|
|
3018
|
+
const timeoutContent = {
|
|
2919
3019
|
text: "Providers took too long to respond. Please optimize your providers or use caching.",
|
|
2920
3020
|
actions: [],
|
|
2921
3021
|
thought: "Provider timeout - pipeline aborted",
|
|
2922
|
-
}
|
|
3022
|
+
};
|
|
3023
|
+
await runtime.applyPipelineHooks("outgoing_before_deliver", outgoingPipelineHookContext(timeoutContent, {
|
|
3024
|
+
source: "simple",
|
|
3025
|
+
roomId: message.roomId,
|
|
3026
|
+
message,
|
|
3027
|
+
}));
|
|
3028
|
+
await callback(timeoutContent);
|
|
2923
3029
|
}
|
|
2924
3030
|
return {
|
|
2925
3031
|
responseContent: null,
|