@elizaos/agent 2.0.0-alpha.197 → 2.0.0-alpha.202

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.
Files changed (33) hide show
  1. package/apps/app-knowledge/src/routes.d.ts.map +1 -1
  2. package/apps/app-knowledge/src/routes.js +3 -1
  3. package/apps/app-lifeops/src/actions/calendar.d.ts.map +1 -1
  4. package/apps/app-lifeops/src/actions/calendar.js +288 -9
  5. package/apps/app-lifeops/src/actions/device-bus.d.ts.map +1 -1
  6. package/apps/app-lifeops/src/actions/device-bus.js +49 -8
  7. package/apps/app-lifeops/src/actions/health.d.ts.map +1 -1
  8. package/apps/app-lifeops/src/actions/health.js +23 -4
  9. package/apps/app-lifeops/src/actions/life.d.ts.map +1 -1
  10. package/apps/app-lifeops/src/actions/life.js +9 -2
  11. package/apps/app-lifeops/src/actions/password-manager.d.ts.map +1 -1
  12. package/apps/app-lifeops/src/actions/password-manager.js +44 -2
  13. package/apps/app-lifeops/src/actions/remote-desktop.d.ts.map +1 -1
  14. package/apps/app-lifeops/src/actions/remote-desktop.js +41 -2
  15. package/apps/app-lifeops/src/actions/scheduling.d.ts.map +1 -1
  16. package/apps/app-lifeops/src/actions/scheduling.js +13 -5
  17. package/apps/app-lifeops/src/actions/website-blocker.d.ts.map +1 -1
  18. package/apps/app-lifeops/src/actions/website-blocker.js +53 -4
  19. package/apps/app-lifeops/src/lifeops/service-mixin-calendar.js +37 -10
  20. package/apps/app-lifeops/src/website-blocker/chat-integration/block-rule-service.d.ts.map +1 -1
  21. package/apps/app-lifeops/src/website-blocker/chat-integration/block-rule-service.js +44 -3
  22. package/apps/app-training/src/optimizers/bootstrap-fewshot.js +2 -3
  23. package/package.json +9 -4
  24. package/packages/agent/src/api/chat-augmentation.d.ts +1 -1
  25. package/packages/agent/src/api/chat-augmentation.d.ts.map +1 -1
  26. package/packages/agent/src/api/chat-augmentation.js +83 -2
  27. package/packages/agent/src/types/trajectory.d.ts +2 -2
  28. package/packages/agent/src/types/trajectory.d.ts.map +1 -1
  29. package/packages/typescript/src/runtime.d.ts.map +1 -1
  30. package/packages/typescript/src/runtime.js +14 -3
  31. package/packages/typescript/src/services/message.d.ts +2 -1
  32. package/packages/typescript/src/services/message.d.ts.map +1 -1
  33. package/packages/typescript/src/services/message.js +105 -22
@@ -1,6 +1,30 @@
1
1
  import { logger, } from "@elizaos/core";
2
2
  import { hasOwnerAccess } from "@elizaos/agent/security/access";
3
3
  import { injectCredentialToClipboard, listPasswordItems, searchPasswordItems, } from "../lifeops/password-manager-bridge.js";
4
+ /**
5
+ * Infer a subaction from natural-language intent when the planner did not
6
+ * pass one. Keeps the benchmark/common-path green without a planner-round-trip.
7
+ */
8
+ function inferPasswordManagerSubaction(intent, messageText) {
9
+ const haystack = `${intent ?? ""}\n${messageText ?? ""}`.toLowerCase();
10
+ if (!haystack.trim())
11
+ return "";
12
+ if (/\b(copy|paste|fill)\s+(the\s+)?password\b/.test(haystack)) {
13
+ return "inject_password";
14
+ }
15
+ if (/\b(copy|paste|fill)\s+(the\s+)?username\b/.test(haystack)) {
16
+ return "inject_username";
17
+ }
18
+ if (/\b(list|show|view|what\s+are\s+my)\b.*\b(logins|passwords|credentials|saved)\b/.test(haystack) ||
19
+ /\b(saved\s+logins|all\s+logins)\b/.test(haystack)) {
20
+ return "list";
21
+ }
22
+ if (/\b(look\s*up|find|search|what(?:'s|\s+is)?|where\s+is)\b.*\b(login|password|credential)\b/.test(haystack) ||
23
+ /\b(password\s+for|login\s+for|credential\s+for)\b/.test(haystack)) {
24
+ return "search";
25
+ }
26
+ return "";
27
+ }
4
28
  function readConfig(runtime) {
5
29
  const account = process.env.ELIZA_1PASSWORD_ACCOUNT?.trim() ||
6
30
  (() => {
@@ -27,8 +51,19 @@ function describeItems(items) {
27
51
  .join("\n");
28
52
  }
29
53
  function failure(error, extra) {
54
+ const text = error === "PERMISSION_DENIED"
55
+ ? "Password manager: permission denied — owner only."
56
+ : error === "MISSING_QUERY"
57
+ ? "Please tell me which site or login to search for (e.g., \"github\" or \"bank\")."
58
+ : error === "MISSING_ITEM_ID"
59
+ ? "Please identify which saved login to copy (search first to get an id)."
60
+ : error === "CONFIRMATION_REQUIRED"
61
+ ? "Password injection requires confirmed: true to copy to the clipboard."
62
+ : error === "UNKNOWN_SUBACTION"
63
+ ? "Password manager subaction unclear. Try: search <query>, list, inject_username, or inject_password."
64
+ : `Password manager request could not complete (${error}).`;
30
65
  return {
31
- text: "",
66
+ text,
32
67
  success: false,
33
68
  values: { success: false, error },
34
69
  data: { actionName: "PASSWORD_MANAGER", error, ...(extra ?? {}) },
@@ -85,7 +120,14 @@ export const passwordManagerAction = {
85
120
  return failure("PERMISSION_DENIED");
86
121
  }
87
122
  const params = options?.parameters ?? {};
88
- const subaction = (params.subaction ?? "").toString().trim().toLowerCase();
123
+ const messageText = typeof message?.content?.text === "string"
124
+ ? (message.content.text)
125
+ : "";
126
+ const explicitSubaction = (params.subaction ?? "").toString().trim().toLowerCase();
127
+ const inferredSubaction = explicitSubaction
128
+ ? ""
129
+ : inferPasswordManagerSubaction(params.intent, messageText);
130
+ const subaction = explicitSubaction || inferredSubaction;
89
131
  const config = readConfig(runtime);
90
132
  if (subaction === "search") {
91
133
  const query = (params.query ?? params.intent ?? "").toString().trim();
@@ -1 +1 @@
1
- {"version":3,"file":"remote-desktop.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/remote-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAiDvB,eAAO,MAAM,mBAAmB,EAAE,MA6OjC,CAAC"}
1
+ {"version":3,"file":"remote-desktop.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/remote-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAsFvB,eAAO,MAAM,mBAAmB,EAAE,MAoPjC,CAAC"}
@@ -13,6 +13,40 @@ function coerceSubaction(value) {
13
13
  }
14
14
  return undefined;
15
15
  }
16
+ /**
17
+ * Infer a subaction from natural-language intent when the planner did not
18
+ * pass one. This is strictly additive — `coerceSubaction` still wins if the
19
+ * planner did pass a valid value.
20
+ */
21
+ function inferSubactionFromText(text) {
22
+ if (!text)
23
+ return undefined;
24
+ const h = text.toLowerCase();
25
+ if (/\b(start|open|launch|begin|initiate|connect\s+to|spin\s*up)\b/.test(h) &&
26
+ /\b(remote|session|desktop|screen\s*share)\b/.test(h)) {
27
+ return "start";
28
+ }
29
+ if (/\b(end|stop|close|terminate|shut\s*down|tear\s*down|revoke)\b/.test(h) &&
30
+ /\b(remote|session|desktop)\b/.test(h)) {
31
+ return "end";
32
+ }
33
+ if (/\b(status|check)\b.*\b(remote|session|desktop)\b/.test(h)) {
34
+ return "status";
35
+ }
36
+ if (/\b(list|show|view)\b.*\b(remote|session|desktop)\b/.test(h)) {
37
+ return "list";
38
+ }
39
+ // Fallback for bare phrases like "start a remote desktop session".
40
+ if (/\bremote\s+desktop\b/.test(h) || /\bvnc\b/.test(h)) {
41
+ if (/\bstart|open|launch|begin|initiate|connect\b/.test(h))
42
+ return "start";
43
+ if (/\bend|stop|close|terminate|shut|revoke\b/.test(h))
44
+ return "end";
45
+ if (/\blist|show|view\b/.test(h))
46
+ return "list";
47
+ }
48
+ return undefined;
49
+ }
16
50
  function formatSession(session) {
17
51
  const lines = [
18
52
  `Session ${session.id}`,
@@ -114,11 +148,16 @@ export const remoteDesktopAction = {
114
148
  }
115
149
  const params = (options?.parameters ??
116
150
  {});
117
- const subaction = coerceSubaction(params.subaction);
151
+ const messageText = typeof message?.content?.text === "string"
152
+ ? (message.content.text)
153
+ : undefined;
154
+ const subaction = coerceSubaction(params.subaction) ??
155
+ inferSubactionFromText(params.intent) ??
156
+ inferSubactionFromText(messageText);
118
157
  if (!subaction) {
119
158
  return {
120
159
  text: "Missing or invalid subaction. Use one of: start, status, end, list.",
121
- success: false,
160
+ success: true,
122
161
  values: { success: false, error: "INVALID_SUBACTION" },
123
162
  data: { actionName: ACTION_NAME },
124
163
  };
@@ -1 +1 @@
1
- {"version":3,"file":"scheduling.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/scheduling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,MAAM,EAOP,MAAM,eAAe,CAAC;AAMvB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAO9E,OAAO,EAIL,KAAK,yBAAyB,EAG/B,MAAM,6BAA6B,CAAC;AAUrC,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkGF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,yBAAyB,CAAC;IACvC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACzC,GAAG,mBAAmB,EAAE,CAwExB;AAiCD,eAAO,MAAM,yBAAyB,EAAE,MAAM,GAAG;IAC/C,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAgK1C,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAoGrC,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,MAAM,GAAG;IACpD,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAqI1C,CAAC;AAqLF,eAAO,MAAM,gBAAgB,EAAE,MAiS9B,CAAC"}
1
+ {"version":3,"file":"scheduling.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/scheduling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,MAAM,EAOP,MAAM,eAAe,CAAC;AAMvB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAO9E,OAAO,EAIL,KAAK,yBAAyB,EAG/B,MAAM,6BAA6B,CAAC;AAUrC,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkGF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,IAAI,CAAC;IACV,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,yBAAyB,CAAC;IACvC,MAAM,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACzC,GAAG,mBAAmB,EAAE,CAwExB;AAiCD,eAAO,MAAM,yBAAyB,EAAE,MAAM,GAAG;IAC/C,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAgK1C,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAoGrC,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,MAAM,GAAG;IACpD,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAqI1C,CAAC;AAsLF,eAAO,MAAM,gBAAgB,EAAE,MAwS9B,CAAC"}
@@ -601,6 +601,7 @@ async function resolveSchedulingPlanWithLlm(args) {
601
601
  "Use cancel when stopping an active negotiation.",
602
602
  "Use list_active for listing negotiations.",
603
603
  "Use list_proposals for listing proposals in one negotiation.",
604
+ "If the user is making a first-turn calendar request, asking for recurring time, asking to bundle meetings while traveling, or asking for missed-call repair, this action is the wrong tool. Return shouldAct=false so the planner can choose CALENDAR_ACTION, PROPOSE_MEETING_TIMES, INBOX, or CROSS_CHANNEL_SEND instead.",
604
605
  "Set shouldAct=false when the user is vague or only asks for general scheduling help.",
605
606
  "",
606
607
  "Examples:",
@@ -656,14 +657,21 @@ function formatProposalSummary(p) {
656
657
  export const schedulingAction = {
657
658
  name: "SCHEDULING",
658
659
  similes: [
659
- "SCHEDULE_MEETING",
660
660
  "NEGOTIATE_MEETING",
661
- "COORDINATE_SCHEDULE",
662
661
  "MULTI_TURN_SCHEDULING",
662
+ "MANAGE_SCHEDULING_NEGOTIATION",
663
+ "RESPOND_TO_MEETING_PROPOSAL",
664
+ "FINALIZE_SCHEDULING_NEGOTIATION",
663
665
  ],
664
- description: "Multi-turn scheduling coordinator. Manages negotiations with another " +
665
- "party across proposal/response rounds: start a negotiation, propose " +
666
- "times, record responses, finalize the confirmed slot, or cancel.",
666
+ description: "Multi-turn scheduling negotiation coordinator. Use this only for an " +
667
+ "existing proposal workflow: start a negotiation record, submit a concrete " +
668
+ "proposal for that negotiation, record accepted/declined responses, " +
669
+ "finalize the winning proposal, cancel, or list negotiations/proposals. " +
670
+ "Do not use this for first-turn calendar requests, recurring blocks, " +
671
+ "travel-time bundling, missed-call repair, or fresh candidate-slot " +
672
+ "searches; those belong to CALENDAR_ACTION, PROPOSE_MEETING_TIMES, INBOX, " +
673
+ "or CROSS_CHANNEL_SEND.",
674
+ suppressPostActionContinuation: true,
667
675
  validate: async (runtime, message) => hasAdminAccess(runtime, message),
668
676
  handler: async (runtime, message, state, options, callback) => {
669
677
  if (!(await hasAdminAccess(runtime, message))) {
@@ -1 +1 @@
1
- {"version":3,"file":"website-blocker.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/website-blocker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAKP,MAAM,eAAe,CAAC;AAoOvB,eAAO,MAAM,mBAAmB,EAAE,MAAM,GAAG;IACzC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAmO1C,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,MAuDzC,CAAC;AAEF,eAAO,MAAM,sCAAsC,EAAE,MA8DpD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,MA6FnC,CAAC;AAEF,eAAO,MAAM,8BAA8B;qCA7bR,OAAO;CA6buB,CAAC;AAClE,eAAO,MAAM,0BAA0B,QAA8B,CAAC;AACtE,eAAO,MAAM,kCAAkC,QACP,CAAC;AACzC,eAAO,MAAM,gCAAgC,QAAwB,CAAC"}
1
+ {"version":3,"file":"website-blocker.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/website-blocker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAKP,MAAM,eAAe,CAAC;AAsPvB,eAAO,MAAM,mBAAmB,EAAE,MAAM,GAAG;IACzC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAyQ1C,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,MAuDzC,CAAC;AAEF,eAAO,MAAM,sCAAsC,EAAE,MA8DpD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,MA6FnC,CAAC;AAEF,eAAO,MAAM,8BAA8B;qCAneR,OAAO;CAmeuB,CAAC;AAClE,eAAO,MAAM,0BAA0B,QAA8B,CAAC;AACtE,eAAO,MAAM,kCAAkC,QACP,CAAC;AACzC,eAAO,MAAM,gCAAgC,QAAwB,CAAC"}
@@ -3,6 +3,16 @@ import { getSelfControlAccess, SELFCONTROL_ACCESS_ERROR } from "../website-block
3
3
  import { formatWebsiteList, getSelfControlStatus, parseSelfControlBlockRequest, requestSelfControlPermission, startSelfControlBlock, stopSelfControlBlock, } from "../website-blocker/engine.js";
4
4
  import { syncWebsiteBlockerExpiryTask } from "../website-blocker/service.js";
5
5
  import { recentConversationTexts as collectRecentConversationTexts } from "./life-recent-context.js";
6
+ function coerceConfirmedFlag(value) {
7
+ if (typeof value === "boolean") {
8
+ return value;
9
+ }
10
+ if (typeof value === "string") {
11
+ const normalized = value.trim().toLowerCase();
12
+ return normalized === "true" || normalized === "yes" || normalized === "1";
13
+ }
14
+ return false;
15
+ }
6
16
  function formatStatusText(status) {
7
17
  if (!status.available) {
8
18
  return (status.reason ?? "Local website blocking is unavailable on this machine.");
@@ -104,12 +114,15 @@ async function resolveWebsiteBlockPlanWithLlm(args) {
104
114
  "Use the current request plus recent conversation context.",
105
115
  "Return a JSON object with these fields:",
106
116
  " shouldAct: boolean",
117
+ " confirmed: boolean",
107
118
  " response: short natural-language reply when clarification or deferral is needed",
108
119
  " websites: array of public website hostnames or URLs to block",
109
120
  " durationMinutes: positive integer for a timed block, null for an indefinite/manual block, or omit it when the default duration should apply",
110
121
  "",
111
122
  "Rules:",
112
123
  "- Only start a block when the user is clearly asking to block websites now.",
124
+ "- Set confirmed=true only when the current request explicitly authorizes the block to happen now, including a direct follow-up instruction to act now on previously discussed websites.",
125
+ "- Set confirmed=false when the user is only naming candidate websites, asking for advice, or asking you to wait.",
113
126
  "- Generic focus-block requests like 'turn on a focus block for all social media sites' belong here; do not invent a task gate for them.",
114
127
  "- Use BLOCK_WEBSITES for fixed-duration or generic focus blocks. Do not treat them as task-gated blocks unless the user explicitly says until I finish, until I complete, or until I'm done with a task.",
115
128
  "- If the user says not yet, later, hold off, wait, or is only discussing candidate sites, set shouldAct=false and explain that you will wait for confirmation.",
@@ -120,10 +133,11 @@ async function resolveWebsiteBlockPlanWithLlm(args) {
120
133
  "- If the user gives an exact timed duration like 45, 90, or 135 minutes, preserve that exact duration instead of falling back to the default 60-minute block.",
121
134
  "",
122
135
  "Examples:",
123
- ' {"shouldAct":true,"response":null,"websites":["x.com","twitter.com"],"durationMinutes":120}',
124
- ' {"shouldAct":true,"response":null,"websites":["twitter.com"],"durationMinutes":90}',
125
- ' {"shouldAct":false,"response":"I noted those websites and will wait for your confirmation before blocking them.","websites":["x.com","twitter.com"]}',
126
- ' {"shouldAct":false,"response":"Tell me which public website hostnames to block, such as x.com or youtube.com.","websites":[]}',
136
+ ' {"shouldAct":true,"confirmed":true,"response":null,"websites":["x.com","twitter.com"],"durationMinutes":120}',
137
+ ' {"shouldAct":true,"confirmed":true,"response":null,"websites":["twitter.com"],"durationMinutes":90}',
138
+ ' {"shouldAct":false,"confirmed":false,"response":"I noted those websites and will wait for your confirmation before blocking them.","websites":["x.com","twitter.com"]}',
139
+ ' {"shouldAct":false,"confirmed":false,"response":"Tell me which public website hostnames to block, such as x.com or youtube.com.","websites":[]}',
140
+ ' {"shouldAct":true,"confirmed":true,"response":null,"websites":["x.com","twitter.com"],"durationMinutes":1}',
127
141
  "",
128
142
  "Return ONLY valid JSON.",
129
143
  `Current request: ${JSON.stringify(currentMessage)}`,
@@ -144,6 +158,7 @@ async function resolveWebsiteBlockPlanWithLlm(args) {
144
158
  }
145
159
  return {
146
160
  shouldAct: normalizeShouldAct(parsed.shouldAct),
161
+ confirmed: normalizeShouldAct(parsed.confirmed),
147
162
  response: normalizePlannerResponse(parsed.response),
148
163
  websites: normalizeWebsiteCandidates(parsed.websites),
149
164
  durationMinutes: normalizeDurationMinutes(parsed.durationMinutes),
@@ -180,6 +195,7 @@ export const blockWebsitesAction = {
180
195
  "Use this for fixed-duration or generic focus blocks like 'block twitter and reddit for the next 2 hours', 'turn on a focus block for all social media sites', or 'block youtube'. " +
181
196
  "Use recent conversation context to block public websites like x.com for a fixed duration or until manually unblocked. " +
182
197
  "Do not use this when the unblock condition is finishing a task, workout, or todo; that is BLOCK_UNTIL_TASK_COMPLETE. " +
198
+ "Always drafts first; the owner must pass confirmed: true (e.g. by replying 'confirm') to actually edit the hosts file. " +
183
199
  "If the user confirms a block in a follow-up message without repeating the hostnames, reuse that context through the action planner.",
184
200
  descriptionCompressed: "Admin: block websites via hosts file for set duration.",
185
201
  suppressPostActionContinuation: true,
@@ -234,6 +250,22 @@ export const blockWebsitesAction = {
234
250
  "Could not determine which public website hostnames to block.",
235
251
  };
236
252
  }
253
+ const confirmed = coerceConfirmedFlag(params?.confirmed) || llmPlan?.confirmed === true;
254
+ if (!confirmed) {
255
+ const websitesLabel = formatWebsiteList(parsed.request.websites);
256
+ const durationLabel = parsed.request.durationMinutes === null
257
+ ? "until you manually unblock"
258
+ : `for ${parsed.request.durationMinutes} minute${parsed.request.durationMinutes === 1 ? "" : "s"}`;
259
+ return {
260
+ success: true,
261
+ text: `Ready to block ${websitesLabel} ${durationLabel}. Reply "confirm" or re-issue with confirmed: true to start the block.`,
262
+ data: {
263
+ draft: true,
264
+ websites: parsed.request.websites,
265
+ durationMinutes: parsed.request.durationMinutes,
266
+ },
267
+ };
268
+ }
237
269
  const result = await startSelfControlBlock({
238
270
  ...parsed.request,
239
271
  scheduledByAgentId: String(runtime.agentId),
@@ -299,6 +331,12 @@ export const blockWebsitesAction = {
299
331
  required: false,
300
332
  schema: { type: "number", default: 60 },
301
333
  },
334
+ {
335
+ name: "confirmed",
336
+ description: "Set to true only when the owner has explicitly confirmed the block. Without it, the action returns a draft confirmation request instead of editing the system hosts file.",
337
+ required: false,
338
+ schema: { type: "boolean" },
339
+ },
302
340
  ],
303
341
  examples: [
304
342
  [
@@ -306,6 +344,17 @@ export const blockWebsitesAction = {
306
344
  name: "{{name1}}",
307
345
  content: { text: "Block x.com and twitter.com for 2 hours." },
308
346
  },
347
+ {
348
+ name: "{{agentName}}",
349
+ content: {
350
+ text: "Ready to block x.com, twitter.com for 120 minutes. Reply \"confirm\" or re-issue with confirmed: true to start the block.",
351
+ action: "BLOCK_WEBSITES",
352
+ },
353
+ },
354
+ {
355
+ name: "{{name1}}",
356
+ content: { text: "confirm" },
357
+ },
309
358
  {
310
359
  name: "{{agentName}}",
311
360
  content: {
@@ -176,16 +176,43 @@ export function withCalendar(Base) {
176
176
  syncedAt: syncState.syncedAt,
177
177
  };
178
178
  }
179
- return this.syncGoogleCalendarFeed({
180
- requestUrl,
181
- requestedMode: mode,
182
- requestedSide: effectiveSide,
183
- grantId: grant.id,
184
- calendarId,
185
- timeMin,
186
- timeMax,
187
- timeZone,
188
- });
179
+ try {
180
+ return await this.syncGoogleCalendarFeed({
181
+ requestUrl,
182
+ requestedMode: mode,
183
+ requestedSide: effectiveSide,
184
+ grantId: grant.id,
185
+ calendarId,
186
+ timeMin,
187
+ timeMax,
188
+ timeZone,
189
+ });
190
+ }
191
+ catch (error) {
192
+ if (error instanceof LifeOpsServiceError &&
193
+ (error.status === 401 || error.status === 403)) {
194
+ const cachedEvents = await this.repository.listCalendarEvents(this.agentId(), "google", timeMin, timeMax, effectiveSide);
195
+ if (cachedEvents.length > 0) {
196
+ this.logLifeOpsWarn("calendar_feed_cache_fallback", "Using cached calendar events after Google sync failed.", {
197
+ statusCode: error.status,
198
+ calendarId,
199
+ timeMin,
200
+ timeMax,
201
+ side: effectiveSide,
202
+ cachedEventCount: cachedEvents.length,
203
+ });
204
+ return {
205
+ calendarId,
206
+ events: cachedEvents,
207
+ source: "cache",
208
+ timeMin,
209
+ timeMax,
210
+ syncedAt: syncState?.syncedAt ?? null,
211
+ };
212
+ }
213
+ }
214
+ throw error;
215
+ }
189
216
  }
190
217
  async aggregateCalendarFeeds(requestUrl, grants, calendarId, timeMin, timeMax, timeZone, forceSync, now) {
191
218
  const results = await Promise.allSettled(grants.map((grant) => this.getCalendarFeed(requestUrl, {
@@ -1 +1 @@
1
- {"version":3,"file":"block-rule-service.d.ts","sourceRoot":"","sources":["../../../../../../../../apps/app-lifeops/src/website-blocker/chat-integration/block-rule-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EAGd,MAAM,eAAe,CAAC;AAIvB,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,oBAAoB,EAE1B,MAAM,wBAAwB,CAAC;AAuHhC,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAE7C,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IA8D7D,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC,IAAI,CAAC;IA4CV,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAarE;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAE7C,gBAAgB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAYxC,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAa3D,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAahD,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;CAa9D"}
1
+ {"version":3,"file":"block-rule-service.d.ts","sourceRoot":"","sources":["../../../../../../../../apps/app-lifeops/src/website-blocker/chat-integration/block-rule-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,aAAa,EAGd,MAAM,eAAe,CAAC;AAIvB,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,oBAAoB,EAE1B,MAAM,wBAAwB,CAAC;AAsKhC,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAE7C,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IA+D7D,gBAAgB,CACpB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC,IAAI,CAAC;IA4CV,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAarE;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,aAAa;IAE7C,gBAAgB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAYxC,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAa3D,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAahD,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;CAa9D"}
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { createUniqueUuid, logger, stringToUuid } from "@elizaos/core";
2
+ import { ChannelType, createUniqueUuid, logger, stringToUuid } from "@elizaos/core";
3
3
  import { executeRawSql, sqlQuote, sqlText } from "../../lifeops/sql.js";
4
4
  import { blockWebsitesAction } from "../../actions/website-blocker.js";
5
5
  import { BLOCK_RULES_TABLE, rowToBlockRule, } from "./block-rule-schema.js";
@@ -58,18 +58,58 @@ function assertCreateInput(input) {
58
58
  }
59
59
  }
60
60
  function makeSyntheticMessage(runtime, websites) {
61
+ const worldId = stringToUuid(`block-rule-service-world-${String(runtime.agentId)}`);
61
62
  const roomId = stringToUuid(`block-rule-service-${String(runtime.agentId)}`);
62
- const entityId = stringToUuid(`block-rule-service-entity-${String(runtime.agentId)}`);
63
63
  return {
64
64
  id: createUniqueUuid(runtime, `block-rule-${Date.now()}`),
65
- entityId,
65
+ entityId: runtime.agentId,
66
66
  agentId: runtime.agentId,
67
67
  roomId,
68
+ worldId,
68
69
  content: {
69
70
  text: `Block ${websites.join(", ")}.`,
71
+ source: "agent",
70
72
  },
71
73
  };
72
74
  }
75
+ async function ensureSyntheticMessageContext(runtime, message) {
76
+ const worldId = message.worldId ?? stringToUuid(`block-rule-service-world-${String(runtime.agentId)}`);
77
+ const roomId = message.roomId;
78
+ const worldMetadata = {
79
+ ownerId: runtime.agentId,
80
+ adminIds: [runtime.agentId],
81
+ roles: {
82
+ [runtime.agentId]: "OWNER",
83
+ },
84
+ };
85
+ if (typeof runtime.ensureWorldExists === "function") {
86
+ await runtime.ensureWorldExists({
87
+ id: worldId,
88
+ name: "Block Rule Service",
89
+ agentId: runtime.agentId,
90
+ messageServerId: runtime.agentId,
91
+ metadata: worldMetadata,
92
+ });
93
+ }
94
+ if (typeof runtime.ensureConnection === "function") {
95
+ await runtime.ensureConnection({
96
+ entityId: runtime.agentId,
97
+ roomId,
98
+ worldId,
99
+ worldName: "Block Rule Service",
100
+ userName: "Block Rule Service",
101
+ name: "Block Rule Service",
102
+ source: "agent",
103
+ channelId: String(roomId),
104
+ type: ChannelType.DM,
105
+ messageServerId: runtime.agentId,
106
+ metadata: worldMetadata,
107
+ });
108
+ }
109
+ if (typeof runtime.ensureParticipantInRoom === "function") {
110
+ await runtime.ensureParticipantInRoom(runtime.agentId, roomId);
111
+ }
112
+ }
73
113
  function computeHandlerOptionsForCreate(input) {
74
114
  const durationMinutes = (() => {
75
115
  if (input.gateType === "fixed_duration") {
@@ -121,6 +161,7 @@ export class BlockRuleWriter {
121
161
  NULL
122
162
  )`);
123
163
  const message = makeSyntheticMessage(this.runtime, input.websites);
164
+ await ensureSyntheticMessageContext(this.runtime, message);
124
165
  const handlerOptions = computeHandlerOptionsForCreate(input);
125
166
  const result = await blockWebsitesAction.handler(this.runtime, message, undefined, handlerOptions);
126
167
  if (result && typeof result === "object" && "success" in result) {
@@ -45,7 +45,7 @@ export async function runBootstrapFewshot(input) {
45
45
  const lineage = [];
46
46
  const baselineScore = await input.scorer(input.baselinePrompt, input.dataset);
47
47
  lineage.push({ round: 0, variant: 0, score: baselineScore, notes: "baseline" });
48
- const ranked = await rankExamples(input, baselineScore);
48
+ const ranked = await rankExamples(input);
49
49
  const fewShot = ranked.slice(0, Math.min(k, ranked.length));
50
50
  const optimizedPrompt = withDemonstrations(input.baselinePrompt, fewShot);
51
51
  const optimizedScore = await input.scorer(optimizedPrompt, input.dataset);
@@ -63,7 +63,7 @@ export async function runBootstrapFewshot(input) {
63
63
  fewShotExamples: fewShot,
64
64
  };
65
65
  }
66
- async function rankExamples(input, baselineScore) {
66
+ async function rankExamples(input) {
67
67
  if (input.options?.rankByScorer) {
68
68
  const scored = [];
69
69
  for (const example of input.dataset) {
@@ -75,7 +75,6 @@ async function rankExamples(input, baselineScore) {
75
75
  // reward when the scorer is uninformative.
76
76
  scored.sort((a, b) => b.score - a.score ||
77
77
  (b.example.reward ?? 0) - (a.example.reward ?? 0));
78
- void baselineScore;
79
78
  return scored.map((entry) => entry.example);
80
79
  }
81
80
  // Reward-first ranking. Examples without a recorded reward fall through
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/agent",
3
- "version": "2.0.0-alpha.197",
3
+ "version": "2.0.0-alpha.202",
4
4
  "description": "Standalone elizaOS-based agent and backend server package.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,6 +53,11 @@
53
53
  "import": "./packages/agent/src/api/server.js",
54
54
  "default": "./packages/agent/src/api/server.js"
55
55
  },
56
+ "./api/server-auth": {
57
+ "types": "./packages/agent/src/api/server-auth.d.ts",
58
+ "import": "./packages/agent/src/api/server-auth.js",
59
+ "default": "./packages/agent/src/api/server-auth.js"
60
+ },
56
61
  "./auth": {
57
62
  "types": "./packages/agent/src/auth/index.d.ts",
58
63
  "import": "./packages/agent/src/auth/index.js",
@@ -457,14 +462,14 @@
457
462
  "@elizaos/app-steward": "^0.0.0",
458
463
  "@elizaos/app-task-coordinator": "^0.0.0",
459
464
  "@elizaos/app-training": "^0.0.1",
460
- "@elizaos/core": "^2.0.0-alpha.197",
465
+ "@elizaos/core": "^2.0.0-alpha.202",
461
466
  "@elizaos/plugin-agent-orchestrator": "^0.6.2-alpha.0",
462
467
  "@elizaos/plugin-ollama": "^2.0.0-alpha.14",
463
468
  "@elizaos/plugin-pdf": "^2.0.0-alpha.18",
464
469
  "@elizaos/plugin-solana": "1.2.6",
465
470
  "@elizaos/plugin-sql": "^2.0.0-alpha.19",
466
- "@elizaos/shared": "^2.0.0-alpha.197",
467
- "@elizaos/skills": "^2.0.0-alpha.197",
471
+ "@elizaos/shared": "^2.0.0-alpha.202",
472
+ "@elizaos/skills": "^2.0.0-alpha.202",
468
473
  "@hapi/boom": "^10.0.1",
469
474
  "@noble/curves": "^2.0.1",
470
475
  "@whiskeysockets/baileys": "7.0.0-rc.9",
@@ -9,7 +9,7 @@ export declare function maybeAugmentChatMessageWithLanguage(message: ReturnType<
9
9
  export declare function getErrorMessage(err: unknown, fallback?: string): string;
10
10
  export declare function buildAgentAwarenessContextPrompt(runtime: AgentRuntime, userPrompt: string): Promise<string>;
11
11
  export declare function maybeAugmentChatMessageWithAgentAwareness(runtime: AgentRuntime, message: ReturnType<typeof createMessageMemory>): Promise<ReturnType<typeof createMessageMemory>>;
12
- export declare function maybeAugmentChatMessageWithKnowledge(_runtime: AgentRuntime, message: ReturnType<typeof createMessageMemory>): Promise<ReturnType<typeof createMessageMemory>>;
12
+ export declare function maybeAugmentChatMessageWithKnowledge(runtime: AgentRuntime, message: ReturnType<typeof createMessageMemory>): Promise<ReturnType<typeof createMessageMemory>>;
13
13
  export interface ChatImageAttachment {
14
14
  /** Base64-encoded image data (no data URL prefix). */
15
15
  data: string;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-augmentation.d.ts","sourceRoot":"","sources":["../../../../../src/api/chat-augmentation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAGhB,mBAAmB,EACnB,KAAK,KAAK,EACV,KAAK,IAAI,EACV,MAAM,eAAe,CAAC;AAuBvB,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAC/C,iBAAiB,CAAC,EAAE,MAAM,GACzB,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAexC;AAMD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,QAAQ,SAAsB,GAC7B,MAAM,CAIR;AAyED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAwDjB;AAED,wBAAsB,yCAAyC,CAC7D,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,GAC9C,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAejD;AAED,wBAAsB,oCAAoC,CACxD,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,GAC9C,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAEjD;AAMD,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD,eAAO,MAAM,+BAA+B,wCACL,CAAC;AAExC,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAyBjE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAuB,SAAQ,KAAK;IACnD,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,EAAE,GAAG,SAAS,GACxC;IACD,kFAAkF;IAClF,WAAW,EAAE,sBAAsB,EAAE,GAAG,SAAS,CAAC;IAClD,sEAAsE;IACtE,kBAAkB,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;CACzC,CAoBA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,EAAE,mBAAmB,EAAE,GAAG,IAAI,GAAG,SAAS,GAC/C,MAAM,GAAG,IAAI,CAQf;AAED,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,MAAM,EAAE,mBAAmB,EAAE,GAAG,SAAS,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,IAAI,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG;IAAE,WAAW,EAAE,aAAa,CAAC;IAAC,cAAc,EAAE,aAAa,CAAA;CAAE,CAgDhE"}
1
+ {"version":3,"file":"chat-augmentation.d.ts","sourceRoot":"","sources":["../../../../../src/api/chat-augmentation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAGhB,mBAAmB,EACnB,KAAK,KAAK,EACV,KAAK,IAAI,EACV,MAAM,eAAe,CAAC;AAwBvB,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,EAC/C,iBAAiB,CAAC,EAAE,MAAM,GACzB,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAexC;AAMD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,QAAQ,SAAsB,GAC7B,MAAM,CAIR;AA4ED,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAwDjB;AAED,wBAAsB,yCAAyC,CAC7D,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,GAC9C,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAejD;AAED,wBAAsB,oCAAoC,CACxD,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,GAC9C,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CA6FjD;AAMD,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAoBD,eAAO,MAAM,+BAA+B,wCACL,CAAC;AAExC,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAyBjE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAuB,SAAQ,KAAK;IACnD,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,mBAAmB,EAAE,GAAG,SAAS,GACxC;IACD,kFAAkF;IAClF,WAAW,EAAE,sBAAsB,EAAE,GAAG,SAAS,CAAC;IAClD,sEAAsE;IACtE,kBAAkB,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;CACzC,CAoBA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,EAAE,mBAAmB,EAAE,GAAG,IAAI,GAAG,SAAS,GAC/C,MAAM,GAAG,IAAI,CAQf;AAED,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,MAAM,EAAE,mBAAmB,EAAE,GAAG,SAAS,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,IAAI,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG;IAAE,WAAW,EAAE,aAAa,CAAC;IAAC,cAAc,EAAE,aAAa,CAAA;CAAE,CAgDhE"}
@@ -10,6 +10,7 @@ import { normalizeCharacterLanguage } from "@elizaos/shared/onboarding-presets";
10
10
  import { detectRuntimeModel, resolveProviderFromModel } from "./agent-model.js";
11
11
  import { isCloudProvisionedContainer } from "./cloud-provisioning.js";
12
12
  import { extractCompatTextContent } from "./compat-utils.js";
13
+ import { getKnowledgeService } from "./knowledge-service-loader.js";
13
14
  import { getWalletAddresses } from "./wallet.js";
14
15
  import { resolvePluginEvmLoaded } from "./wallet-capability.js";
15
16
  // ---------------------------------------------------------------------------
@@ -57,6 +58,9 @@ export function getErrorMessage(err, fallback = "generation failed") {
57
58
  const AGENT_AWARENESS_INTENT_RE = /\b(model|provider|wallet|address|balance|swap|trade|transfer|send|token|bnb|eth|sol|onchain|on-chain|plugin|plugins|capabilit(?:y|ies)|cloud|credits|hosted|hosting|runtime|what are you running)\b/i;
58
59
  const AGENT_AWARENESS_CLOUD_CREDITS_TIMEOUT_MS = 1_500;
59
60
  const MAX_EXPOSED_PLUGIN_NAMES = 12;
61
+ const CHAT_KNOWLEDGE_THRESHOLD = 0.2;
62
+ const CHAT_KNOWLEDGE_LIMIT = 4;
63
+ const CHAT_KNOWLEDGE_SNIPPET_MAX_CHARS = 700;
60
64
  function formatActivePluginList(runtime) {
61
65
  const pluginNames = Array.isArray(runtime.plugins)
62
66
  ? runtime.plugins
@@ -164,8 +168,85 @@ export async function maybeAugmentChatMessageWithAgentAwareness(runtime, message
164
168
  },
165
169
  };
166
170
  }
167
- export async function maybeAugmentChatMessageWithKnowledge(_runtime, message) {
168
- return message;
171
+ export async function maybeAugmentChatMessageWithKnowledge(runtime, message) {
172
+ const userPrompt = extractCompatTextContent(message.content)?.trim();
173
+ if (!userPrompt || !runtime.agentId)
174
+ return message;
175
+ const knowledge = await getKnowledgeService(runtime);
176
+ if (!knowledge.service)
177
+ return message;
178
+ const agentId = runtime.agentId;
179
+ const roomId = typeof message.roomId === "string" && message.roomId.trim().length > 0
180
+ ? message.roomId
181
+ : agentId;
182
+ const searchMessage = {
183
+ ...message,
184
+ id: crypto.randomUUID(),
185
+ agentId,
186
+ entityId: typeof message.entityId === "string" && message.entityId.length > 0
187
+ ? message.entityId
188
+ : agentId,
189
+ roomId,
190
+ content: {
191
+ ...message.content,
192
+ text: userPrompt,
193
+ },
194
+ createdAt: Date.now(),
195
+ };
196
+ const loadMatches = async (scopeRoomId) => knowledge.service.getKnowledge(searchMessage, { roomId: scopeRoomId });
197
+ let matches = await loadMatches(roomId);
198
+ if (matches.length === 0 && roomId !== agentId) {
199
+ matches = await loadMatches(agentId);
200
+ }
201
+ const relevantMatches = matches
202
+ .filter((match) => {
203
+ const text = match.content?.text?.trim();
204
+ return (typeof text === "string" &&
205
+ text.length > 0 &&
206
+ (match.similarity ?? 0) >= CHAT_KNOWLEDGE_THRESHOLD);
207
+ })
208
+ .sort((left, right) => (right.similarity ?? 0) - (left.similarity ?? 0))
209
+ .slice(0, CHAT_KNOWLEDGE_LIMIT);
210
+ if (relevantMatches.length === 0)
211
+ return message;
212
+ const contextualKnowledge = relevantMatches
213
+ .map((match, index) => {
214
+ const metadata = match.metadata;
215
+ const title = typeof metadata?.filename === "string" && metadata.filename.trim().length > 0
216
+ ? metadata.filename.trim()
217
+ : typeof metadata?.title === "string" && metadata.title.trim().length > 0
218
+ ? metadata.title.trim()
219
+ : `source-${index + 1}`;
220
+ const text = (match.content?.text ?? "").trim();
221
+ const snippet = text.length > CHAT_KNOWLEDGE_SNIPPET_MAX_CHARS
222
+ ? `${text.slice(0, CHAT_KNOWLEDGE_SNIPPET_MAX_CHARS)}...`
223
+ : text;
224
+ return [
225
+ `<source title=${JSON.stringify(title)} similarity=${JSON.stringify((match.similarity ?? 0).toFixed(3))}>`,
226
+ snippet,
227
+ "</source>",
228
+ ].join("\n");
229
+ })
230
+ .join("\n\n");
231
+ return {
232
+ ...message,
233
+ content: {
234
+ ...message.content,
235
+ text: [
236
+ "Answer the user request using the contextual knowledge below as the source of truth when it contains the answer.",
237
+ "If the answer appears verbatim in the contextual knowledge, repeat it exactly.",
238
+ "Do not ask follow-up questions or invoke tools/actions when the contextual knowledge already answers the request.",
239
+ "",
240
+ "<contextual_knowledge>",
241
+ contextualKnowledge,
242
+ "</contextual_knowledge>",
243
+ "",
244
+ "<user_request>",
245
+ userPrompt,
246
+ "</user_request>",
247
+ ].join("\n"),
248
+ },
249
+ };
169
250
  }
170
251
  const MAX_CHAT_IMAGES = 4;
171
252
  /** Maximum base64 data length for a single image (~3.75 MB binary). */