@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 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../../../apps/app-knowledge/src/routes.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAahE,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE1F,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAC9B;AA0JD,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,eAAe,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AA0GtE,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,eAAe,GAAG,IAAI,GAC3B,IAAI,CAEN;AAsWD,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,OAAO,CAAC,CAylBlB"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../../../apps/app-knowledge/src/routes.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAahE,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE1F,MAAM,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CAC9B;AA0JD,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,KAAK,eAAe,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AA0GtE,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,eAAe,GAAG,IAAI,GAC3B,IAAI,CAEN;AAsWD,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,OAAO,CAAC,CA6lBlB"}
@@ -684,7 +684,9 @@ export async function handleKnowledgeRoutes(ctx) {
684
684
  const result = await service.addKnowledge({
685
685
  agentId,
686
686
  worldId: agentId,
687
- roomId: agentId,
687
+ roomId: typeof document.roomId === "string" && document.roomId.trim().length > 0
688
+ ? document.roomId.trim()
689
+ : agentId,
688
690
  entityId: agentId,
689
691
  clientDocumentId: "", // Will be generated
690
692
  contentType,
@@ -1 +1 @@
1
- {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAKN,aAAa,EACb,MAAM,EACN,KAAK,EACN,MAAM,eAAe,CAAC;AAyCvB,KAAK,iBAAiB,GAClB,MAAM,GACN,YAAY,GACZ,eAAe,GACf,cAAc,GACd,cAAc,GACd,cAAc,GACd,aAAa,CAAC;AAuBlB,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAg0CF,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,SAA2B,GAClC,OAAO,CAAC,eAAe,CAAC,CA6J1B;AAu8BD,eAAO,MAAM,cAAc,EAAE,MAAM,GAAG;IACpC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CA6jC1C,CAAC"}
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/calendar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAKN,aAAa,EACb,MAAM,EACN,KAAK,EACN,MAAM,eAAe,CAAC;AAyCvB,KAAK,iBAAiB,GAClB,MAAM,GACN,YAAY,GACZ,eAAe,GACf,cAAc,GACd,cAAc,GACd,cAAc,GACd,aAAa,CAAC;AAuBlB,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAkjDF,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,MAAM,EAAE,MAAM,EACd,QAAQ,SAA2B,GAClC,OAAO,CAAC,eAAe,CAAC,CA6J1B;AAigCD,eAAO,MAAM,cAAc,EAAE,MAAM,GAAG;IACpC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAgnC1C,CAAC"}
@@ -3,6 +3,7 @@ import { resolveDefaultTimeZone } from "../lifeops/defaults.js";
3
3
  import { LifeOpsService, LifeOpsServiceError } from "../lifeops/service.js";
4
4
  import { addDaysToLocalDate, buildUtcDateFromLocalParts, getWeekdayForLocalDate, getZonedDateParts, } from "../lifeops/time.js";
5
5
  import { renderGroundedActionReply } from "@elizaos/agent/actions/grounded-action-reply";
6
+ import { recentConversationTexts as collectRecentConversationTexts } from "./life-recent-context.js";
6
7
  import { calendarReadUnavailableMessage, calendarWriteUnavailableMessage, detailArray, detailBoolean, detailNumber, detailString, formatCalendarEventDateTime, formatCalendarFeed, formatNextEventContext, getGoogleCapabilityStatus, hasLifeOpsAccess, INTERNAL_URL, messageText, toActionData, } from "./lifeops-google-helpers.js";
7
8
  import { looksLikeCalendarObservation } from "./non-actionable-request.js";
8
9
  const CALENDAR_SUBACTION_VALUES = [
@@ -227,6 +228,20 @@ function normalizeCalendarReadSubaction(value) {
227
228
  }
228
229
  return null;
229
230
  }
231
+ function normalizeCalendarLookupReadSubaction(value) {
232
+ if (value === "next_event" || value === "search_events") {
233
+ return value;
234
+ }
235
+ return null;
236
+ }
237
+ function normalizeCalendarMutationSubaction(value) {
238
+ if (value === "create_event" ||
239
+ value === "update_event" ||
240
+ value === "delete_event") {
241
+ return value;
242
+ }
243
+ return null;
244
+ }
230
245
  function normalizeCalendarReadResolution(parsed) {
231
246
  if (!parsed) {
232
247
  return null;
@@ -294,8 +309,119 @@ async function disambiguateCalendarReadPlanWithLlm(args) {
294
309
  return null;
295
310
  }
296
311
  }
312
+ async function resolveCalendarLookupBoundaryWithLlm(args) {
313
+ const prompt = [
314
+ "Resolve this calendar lookup intent.",
315
+ "The user may speak in any language.",
316
+ "Choose exactly one subaction: next_event or search_events.",
317
+ "next_event means the user wants only the single nearest upcoming meeting, appointment, or event.",
318
+ "search_events means the user wants to find matching calendar events by title, attendee, place, trip, keyword, or date.",
319
+ "If the request asks for the next upcoming meeting or appointment, prefer next_event even when the noun could also be searched.",
320
+ "If the request is about flights, dentists, people, trip plans, named events, or general event lookup, choose search_events.",
321
+ "",
322
+ "Examples:",
323
+ ' "What\'s my next meeting?" -> {"subaction":"next_event"}',
324
+ ' "When is my next appointment?" -> {"subaction":"next_event"}',
325
+ ' "次のミーティングはいつですか?" -> {"subaction":"next_event"}',
326
+ ' "¿Cuál es mi próxima reunión?" -> {"subaction":"next_event"}',
327
+ ' "find my return flight" -> {"subaction":"search_events"}',
328
+ ' "when is the dentist" -> {"subaction":"search_events"}',
329
+ ' "帰りの便を探して" -> {"subaction":"search_events"}',
330
+ "",
331
+ "Return ONLY valid JSON with exactly this field:",
332
+ " subaction: next_event or search_events",
333
+ "",
334
+ `Current request: ${JSON.stringify(args.currentMessage)}`,
335
+ `Resolved intent: ${JSON.stringify(args.intent)}`,
336
+ `Recent conversation: ${JSON.stringify(args.recentConversation)}`,
337
+ `Current candidate: ${JSON.stringify(args.candidateSubaction)}`,
338
+ ].join("\n");
339
+ try {
340
+ const result = await args.runtime.useModel(ModelType.TEXT_LARGE, {
341
+ prompt,
342
+ });
343
+ const raw = typeof result === "string" ? result : "";
344
+ const parsed = parseKeyValueXml(raw) ??
345
+ parseJSONObjectFromText(raw);
346
+ return normalizeCalendarLookupReadSubaction(parsed?.subaction);
347
+ }
348
+ catch (error) {
349
+ args.runtime.logger?.warn?.({
350
+ src: "action:calendar",
351
+ error: error instanceof Error ? error.message : String(error),
352
+ }, "Calendar lookup boundary model call failed");
353
+ return null;
354
+ }
355
+ }
356
+ async function resolveCalendarMutationBoundaryWithLlm(args) {
357
+ const prompt = [
358
+ "Resolve whether this calendar request is a mutation.",
359
+ "The user may speak in any language.",
360
+ "Choose exactly one subaction: create_event, update_event, delete_event, or null.",
361
+ "create_event means schedule, add, book, or put a new event on the calendar.",
362
+ "update_event means rename, reschedule, move, or otherwise edit an existing event.",
363
+ "delete_event means delete, cancel, remove, or clear an existing event.",
364
+ "Use null when the request is only reading the calendar, searching events, discussing plans, or asking for general help.",
365
+ "Prefer create_event when the user gives a time/date and asks to add or schedule a meeting or appointment, regardless of language.",
366
+ "",
367
+ "Examples:",
368
+ ' "Schedule a meeting with Alex at 3pm tomorrow" -> {"subaction":"create_event"}',
369
+ ' "Agenda una reunión con Alex mañana a las 3pm" -> {"subaction":"create_event"}',
370
+ ' "明日の午後3時にアレックスとのミーティングを入れて" -> {"subaction":"create_event"}',
371
+ ' "Reschedule the dentist to Friday" -> {"subaction":"update_event"}',
372
+ ' "Cambia la cita del dentista al viernes" -> {"subaction":"update_event"}',
373
+ ' "Delete the team meeting tomorrow" -> {"subaction":"delete_event"}',
374
+ ' "今日の予定は何ですか?" -> {"subaction":null}',
375
+ "",
376
+ "Return ONLY valid JSON with exactly this field:",
377
+ " subaction: create_event, update_event, delete_event, or null",
378
+ "",
379
+ `Current request: ${JSON.stringify(args.currentMessage)}`,
380
+ `Resolved intent: ${JSON.stringify(args.intent)}`,
381
+ `Recent conversation: ${JSON.stringify(args.recentConversation)}`,
382
+ `Current candidate: ${JSON.stringify(args.candidateSubaction)}`,
383
+ ].join("\n");
384
+ try {
385
+ const result = await args.runtime.useModel(ModelType.TEXT_LARGE, {
386
+ prompt,
387
+ });
388
+ const raw = typeof result === "string" ? result : "";
389
+ const parsed = parseKeyValueXml(raw) ??
390
+ parseJSONObjectFromText(raw);
391
+ return normalizeCalendarMutationSubaction(parsed?.subaction);
392
+ }
393
+ catch (error) {
394
+ args.runtime.logger?.warn?.({
395
+ src: "action:calendar",
396
+ error: error instanceof Error ? error.message : String(error),
397
+ }, "Calendar mutation boundary model call failed");
398
+ return null;
399
+ }
400
+ }
297
401
  async function finalizeCalendarPlan(args) {
298
402
  const { runtime, currentMessage, intent, recentConversation, plan } = args;
403
+ if (plan?.subaction !== "create_event" &&
404
+ plan?.subaction !== "update_event" &&
405
+ plan?.subaction !== "delete_event") {
406
+ const mutationSubaction = await resolveCalendarMutationBoundaryWithLlm({
407
+ runtime,
408
+ currentMessage,
409
+ intent,
410
+ recentConversation,
411
+ candidateSubaction: plan?.subaction ?? null,
412
+ });
413
+ if (mutationSubaction) {
414
+ return {
415
+ ...(plan ?? {
416
+ queries: [],
417
+ shouldAct: null,
418
+ }),
419
+ subaction: mutationSubaction,
420
+ shouldAct: true,
421
+ response: undefined,
422
+ };
423
+ }
424
+ }
299
425
  if (!shouldDisambiguateCalendarReadPlan(plan)) {
300
426
  return (plan ?? {
301
427
  subaction: null,
@@ -317,10 +443,24 @@ async function finalizeCalendarPlan(args) {
317
443
  shouldAct: null,
318
444
  });
319
445
  }
446
+ let finalReadSubaction = resolvedReadPlan.subaction;
447
+ if (finalReadSubaction === "next_event" ||
448
+ finalReadSubaction === "search_events") {
449
+ const boundarySubaction = await resolveCalendarLookupBoundaryWithLlm({
450
+ runtime,
451
+ currentMessage,
452
+ intent,
453
+ recentConversation,
454
+ candidateSubaction: finalReadSubaction,
455
+ });
456
+ if (boundarySubaction) {
457
+ finalReadSubaction = boundarySubaction;
458
+ }
459
+ }
320
460
  if (plan) {
321
461
  return {
322
462
  ...plan,
323
- subaction: resolvedReadPlan.subaction,
463
+ subaction: finalReadSubaction,
324
464
  tripLocation: resolvedReadPlan.tripLocation ?? plan.tripLocation,
325
465
  queries: dedupeCalendarQueries([
326
466
  ...plan.queries,
@@ -331,7 +471,7 @@ async function finalizeCalendarPlan(args) {
331
471
  };
332
472
  }
333
473
  return {
334
- subaction: resolvedReadPlan.subaction,
474
+ subaction: finalReadSubaction,
335
475
  queries: dedupeCalendarQueries([resolvedReadPlan.tripLocation]),
336
476
  shouldAct: true,
337
477
  tripLocation: resolvedReadPlan.tripLocation,
@@ -667,6 +807,34 @@ function buildLocalDayRange(timeZone, startOffsetDays, endOffsetDaysExclusive) {
667
807
  const localToday = getLocalTodayDate(timeZone);
668
808
  return buildLocalDateRange(timeZone, addDaysToLocalDate(localToday, startOffsetDays), addDaysToLocalDate(localToday, endOffsetDaysExclusive));
669
809
  }
810
+ function formatExplicitCalendarDateLabel(args) {
811
+ return new Intl.DateTimeFormat("en-US", {
812
+ timeZone: args.timeZone,
813
+ month: "long",
814
+ day: "numeric",
815
+ year: "numeric",
816
+ }).format(buildUtcDateFromLocalParts(args.timeZone, {
817
+ year: args.date.year,
818
+ month: args.date.month,
819
+ day: args.date.day,
820
+ hour: 12,
821
+ minute: 0,
822
+ second: 0,
823
+ }));
824
+ }
825
+ function resolveExplicitCalendarDateWindow(intent, timeZone) {
826
+ const explicitDate = parseExplicitLocalDate(intent, timeZone);
827
+ if (!explicitDate) {
828
+ return null;
829
+ }
830
+ return {
831
+ ...buildLocalDateRange(timeZone, explicitDate, addDaysToLocalDate(explicitDate, 1)),
832
+ label: `on ${formatExplicitCalendarDateLabel({
833
+ date: explicitDate,
834
+ timeZone,
835
+ })}`,
836
+ };
837
+ }
670
838
  function compareLocalDates(left, right) {
671
839
  if (left.year !== right.year) {
672
840
  return left.year - right.year;
@@ -886,7 +1054,7 @@ function resolveCalendarLlmWindow(llmPlan) {
886
1054
  function buildWideLookupRange(timeZone) {
887
1055
  return buildLocalDayRange(timeZone, -365, 365 * 5);
888
1056
  }
889
- function resolveCalendarWindow(_intent, details, forSearch, llmPlan) {
1057
+ function resolveCalendarWindow(intent, details, forSearch, llmPlan) {
890
1058
  const timeMin = detailString(details, "timeMin");
891
1059
  const timeMax = detailString(details, "timeMax");
892
1060
  const calendarId = detailString(details, "calendarId");
@@ -902,6 +1070,7 @@ function resolveCalendarWindow(_intent, details, forSearch, llmPlan) {
902
1070
  forceSync,
903
1071
  },
904
1072
  label: detailString(details, "label") ?? "for the requested window",
1073
+ explicitWindow: true,
905
1074
  };
906
1075
  }
907
1076
  const llmWindow = resolveCalendarLlmWindow(llmPlan);
@@ -915,6 +1084,21 @@ function resolveCalendarWindow(_intent, details, forSearch, llmPlan) {
915
1084
  timeMax: llmWindow.timeMax,
916
1085
  },
917
1086
  label: llmWindow.label,
1087
+ explicitWindow: true,
1088
+ };
1089
+ }
1090
+ const explicitDateWindow = resolveExplicitCalendarDateWindow(intent, timeZone);
1091
+ if (explicitDateWindow) {
1092
+ return {
1093
+ request: {
1094
+ calendarId,
1095
+ timeZone,
1096
+ forceSync,
1097
+ timeMin: explicitDateWindow.timeMin,
1098
+ timeMax: explicitDateWindow.timeMax,
1099
+ },
1100
+ label: explicitDateWindow.label,
1101
+ explicitWindow: true,
918
1102
  };
919
1103
  }
920
1104
  const windowDays = detailNumber(details, "windowDays");
@@ -928,6 +1112,7 @@ function resolveCalendarWindow(_intent, details, forSearch, llmPlan) {
928
1112
  ...buildLocalDayRange(timeZone, 0, days),
929
1113
  },
930
1114
  label: `across the next ${days} days`,
1115
+ explicitWindow: false,
931
1116
  };
932
1117
  }
933
1118
  return {
@@ -938,6 +1123,7 @@ function resolveCalendarWindow(_intent, details, forSearch, llmPlan) {
938
1123
  ...buildLocalDayRange(timeZone, 0, 1),
939
1124
  },
940
1125
  label: "today",
1126
+ explicitWindow: false,
941
1127
  };
942
1128
  }
943
1129
  function resolveTripWindowRequest(details, llmPlan) {
@@ -1160,6 +1346,52 @@ function resolveCalendarSearchQueries(args) {
1160
1346
  ...(args.fallbackQueries ?? []),
1161
1347
  ]);
1162
1348
  }
1349
+ async function recoverCalendarSearchQueriesWithLlm(args) {
1350
+ const prompt = [
1351
+ "Extract up to 3 short calendar search queries from this request.",
1352
+ "Return ONLY valid JSON with this shape:",
1353
+ ' {"queries":["query one","query two"]}',
1354
+ "",
1355
+ "Rules:",
1356
+ "- Queries should be short people, places, trip labels, flight labels, appointment names, or other event lookup phrases.",
1357
+ "- Do not return generic filler like calendar, event, schedule, search, what, tell me, or do i have.",
1358
+ "- When the user is only asking for the agenda on a date or date range and there is no separate search target, return an empty array.",
1359
+ "- The user may speak in any language.",
1360
+ "",
1361
+ "Examples:",
1362
+ ' "do i have any flights to denver" -> {"queries":["flights to denver","denver"]}',
1363
+ ' "puedes buscar si tengo un vuelo a denver" -> {"queries":["vuelo a denver","denver"]}',
1364
+ ' "what event do i have on March 5" -> {"queries":[]}',
1365
+ "",
1366
+ `Current request: ${JSON.stringify(args.currentMessage)}`,
1367
+ `Resolved intent: ${JSON.stringify(args.intent)}`,
1368
+ `Recent conversation: ${JSON.stringify(args.recentConversation)}`,
1369
+ ].join("\n");
1370
+ try {
1371
+ const result = await args.runtime.useModel(ModelType.TEXT_LARGE, {
1372
+ prompt,
1373
+ });
1374
+ const raw = typeof result === "string" ? result : "";
1375
+ const parsed = parseKeyValueXml(raw) ??
1376
+ parseJSONObjectFromText(raw);
1377
+ if (!parsed) {
1378
+ return [];
1379
+ }
1380
+ const rawQueries = Array.isArray(parsed.queries)
1381
+ ? parsed.queries
1382
+ : typeof parsed.queries === "string"
1383
+ ? parsed.queries.split(/\s*\|\|\s*|,|\n/)
1384
+ : [];
1385
+ return dedupeCalendarQueries(rawQueries.filter((value) => typeof value === "string"));
1386
+ }
1387
+ catch (error) {
1388
+ args.runtime.logger?.warn?.({
1389
+ src: "action:calendar",
1390
+ error: error instanceof Error ? error.message : String(error),
1391
+ }, "Calendar search query recovery model call failed");
1392
+ return [];
1393
+ }
1394
+ }
1163
1395
  function normalizeIsShortPreparationFlag(value) {
1164
1396
  if (typeof value === "boolean")
1165
1397
  return value;
@@ -2467,6 +2699,7 @@ export const calendarAction = {
2467
2699
  const baseResolved = resolveCalendarWindow(intent, details, subaction === "search_events", llmPlan);
2468
2700
  const request = baseResolved.request;
2469
2701
  const label = baseResolved.label;
2702
+ const hasExplicitWindow = baseResolved.explicitWindow;
2470
2703
  const feed = await service.getCalendarFeed(INTERNAL_URL, {
2471
2704
  mode: detailString(details, "mode"),
2472
2705
  side: detailString(details, "side"),
@@ -2474,8 +2707,54 @@ export const calendarAction = {
2474
2707
  ...request,
2475
2708
  });
2476
2709
  if (subaction === "search_events") {
2477
- const query = searchQueries[0];
2478
- if (!query || searchQueries.length === 0) {
2710
+ let queriesForSearch = searchQueries;
2711
+ if (queriesForSearch.length === 0) {
2712
+ const recentConversation = (await collectRecentConversationTexts({
2713
+ runtime,
2714
+ message,
2715
+ state,
2716
+ limit: 8,
2717
+ })).join("\n");
2718
+ queriesForSearch = await recoverCalendarSearchQueriesWithLlm({
2719
+ runtime,
2720
+ currentMessage: messageText(message).trim(),
2721
+ intent,
2722
+ recentConversation,
2723
+ });
2724
+ if (queriesForSearch.length === 0) {
2725
+ const recoveredReadPlan = await disambiguateCalendarReadPlanWithLlm({
2726
+ runtime,
2727
+ currentMessage: messageText(message).trim(),
2728
+ intent,
2729
+ recentConversation,
2730
+ candidateSubaction: "search_events",
2731
+ });
2732
+ if (recoveredReadPlan?.subaction === "feed") {
2733
+ const fallback = formatCalendarFeed(feed, label);
2734
+ return respond({
2735
+ success: true,
2736
+ text: await renderReply("feed_results", fallback, {
2737
+ label,
2738
+ events: feed.events,
2739
+ }),
2740
+ data: toActionData(feed),
2741
+ });
2742
+ }
2743
+ }
2744
+ }
2745
+ const query = queriesForSearch[0];
2746
+ if (!query || queriesForSearch.length === 0) {
2747
+ if (hasExplicitWindow) {
2748
+ const fallback = formatCalendarFeed(feed, label);
2749
+ return respond({
2750
+ success: true,
2751
+ text: await renderReply("feed_results", fallback, {
2752
+ label,
2753
+ events: feed.events,
2754
+ }),
2755
+ data: toActionData(feed),
2756
+ });
2757
+ }
2479
2758
  return respond({
2480
2759
  success: false,
2481
2760
  text: await renderReply("clarify_calendar_search", "I couldn't infer what to look for in your calendar yet. Try naming a person, place, trip, or date.", {
@@ -2487,7 +2766,7 @@ export const calendarAction = {
2487
2766
  .map((event) => {
2488
2767
  const matchedQueries = [];
2489
2768
  let score = 0;
2490
- for (const candidateQuery of searchQueries) {
2769
+ for (const candidateQuery of queriesForSearch) {
2491
2770
  const queryScore = scoreCalendarEvent(event, candidateQuery);
2492
2771
  if (queryScore > 0) {
2493
2772
  matchedQueries.push(candidateQuery);
@@ -2512,7 +2791,7 @@ export const calendarAction = {
2512
2791
  .filter((candidate) => candidate.score >= strongestThreshold)
2513
2792
  .map((candidate) => candidate.event);
2514
2793
  if (shouldGroundCalendarSearchWithLlm(query, rankedEvents)) {
2515
- const groundedIds = await groundCalendarSearchMatchesWithLlm(runtime, state, intent, searchQueries, rankedEvents.slice(0, 6));
2794
+ const groundedIds = await groundCalendarSearchMatchesWithLlm(runtime, state, intent, queriesForSearch, rankedEvents.slice(0, 6));
2516
2795
  if (groundedIds) {
2517
2796
  const groundedIdSet = new Set(groundedIds);
2518
2797
  filteredEvents = rankedEvents
@@ -2525,14 +2804,14 @@ export const calendarAction = {
2525
2804
  success: true,
2526
2805
  text: await renderReply("search_results", fallback, {
2527
2806
  query,
2528
- queries: searchQueries,
2807
+ queries: queriesForSearch,
2529
2808
  events: filteredEvents,
2530
2809
  label,
2531
2810
  }),
2532
2811
  data: toActionData({
2533
2812
  ...feed,
2534
2813
  query,
2535
- queries: searchQueries,
2814
+ queries: queriesForSearch,
2536
2815
  events: filteredEvents,
2537
2816
  }),
2538
2817
  });
@@ -1 +1 @@
1
- {"version":3,"file":"device-bus.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/device-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAgBvB,QAAA,MAAM,WAAW,yCAA0C,CAAC;AAC5D,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAQ9C,iBAAS,mBAAmB,CAC1B,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GAAG,SAAS,GAC7D;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAa9C;AAED,iBAAS,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAS1E;AAED,eAAO,MAAM,yBAAyB,EAAE,MAAM,GAAG;IAC/C,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAuN1C,CAAC;AAGF,eAAO,MAAM,UAAU;;;CAGtB,CAAC"}
1
+ {"version":3,"file":"device-bus.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/device-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAiBvB,QAAA,MAAM,WAAW,yCAA0C,CAAC;AAC5D,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AA0D9C,iBAAS,mBAAmB,CAC1B,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GAAG,SAAS,GAC7D;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAa9C;AAED,iBAAS,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAS1E;AAED,eAAO,MAAM,yBAAyB,EAAE,MAAM,GAAG;IAC/C,8BAA8B,CAAC,EAAE,OAAO,CAAC;CA4N1C,CAAC;AAGF,eAAO,MAAM,UAAU;;;CAGtB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { logger, } from "@elizaos/core";
2
2
  import { hasOwnerAccess } from "@elizaos/agent/security/access";
3
+ import { broadcastIntent } from "../lifeops/intent-sync.js";
3
4
  /**
4
5
  * Cross-device intent bus agent-side action.
5
6
  *
@@ -12,6 +13,40 @@ import { hasOwnerAccess } from "@elizaos/agent/security/access";
12
13
  const DEVICE_BUS_URL_ENV = "MILADY_DEVICE_BUS_URL";
13
14
  const DEVICE_BUS_TOKEN_ENV = "MILADY_DEVICE_BUS_TOKEN";
14
15
  const KNOWN_KINDS = ["alarm", "reminder", "block"];
16
+ function coercePayload(payload) {
17
+ return payload && typeof payload === "object"
18
+ ? payload
19
+ : {};
20
+ }
21
+ function readPayloadString(payload, keys) {
22
+ for (const key of keys) {
23
+ const value = payload[key];
24
+ if (typeof value === "string" && value.trim().length > 0) {
25
+ return value.trim();
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+ function mapLocalIntentKind(kind) {
31
+ return kind === "block" ? "attention_request" : "routine_reminder";
32
+ }
33
+ async function publishLocalFallbackIntent(runtime, kind, payload) {
34
+ const title = readPayloadString(payload, ["title", "label", "subject"]) ??
35
+ `Device ${kind}`;
36
+ const body = readPayloadString(payload, ["body", "message", "text", "description"]) ??
37
+ `Published ${kind} intent for local delivery.`;
38
+ return await broadcastIntent(runtime, {
39
+ kind: mapLocalIntentKind(kind),
40
+ title,
41
+ body,
42
+ priority: kind === "alarm" || kind === "block" ? "high" : "medium",
43
+ metadata: {
44
+ sourceAction: "PUBLISH_DEVICE_INTENT",
45
+ deviceBusKind: kind,
46
+ payload,
47
+ },
48
+ });
49
+ }
15
50
  function readDeviceBusConfig(runtime) {
16
51
  const readString = (key) => {
17
52
  const env = process.env[key]?.trim();
@@ -78,20 +113,26 @@ export const publishDeviceIntentAction = {
78
113
  }
79
114
  const params = options?.parameters ?? {};
80
115
  const kind = normalizeKind(params.kind) ?? "reminder";
81
- const payload = params.payload && typeof params.payload === "object"
82
- ? params.payload
83
- : {};
116
+ const payload = coercePayload(params.payload);
84
117
  const config = readDeviceBusConfig(runtime);
85
118
  if (!config) {
86
- logger.warn({ action: "PUBLISH_DEVICE_INTENT", kind }, "[PUBLISH_DEVICE_INTENT] device bus not configured (set MILADY_DEVICE_BUS_URL)");
119
+ logger.warn({ action: "PUBLISH_DEVICE_INTENT", kind }, "[PUBLISH_DEVICE_INTENT] device bus not configured; falling back to local intent store");
120
+ const localIntent = await publishLocalFallbackIntent(runtime, kind, payload);
87
121
  return {
88
- text: "",
89
- success: false,
90
- values: { success: false, reason: "device-bus-not-configured", kind },
122
+ text: `Queued ${kind} intent locally for device delivery.`,
123
+ success: true,
124
+ values: {
125
+ success: true,
126
+ reason: "device-bus-local-fallback",
127
+ kind,
128
+ intentId: localIntent.id,
129
+ },
91
130
  data: {
92
131
  actionName: "PUBLISH_DEVICE_INTENT",
93
- reason: "device-bus-not-configured",
132
+ reason: "device-bus-local-fallback",
94
133
  kind,
134
+ intentId: localIntent.id,
135
+ transport: "local-fallback",
95
136
  },
96
137
  };
97
138
  }
@@ -1 +1 @@
1
- {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/health.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,MAAM,EAOP,MAAM,eAAe,CAAC;AAqOvB,eAAO,MAAM,YAAY,EAAE,MA8M1B,CAAC"}
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/health.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,MAAM,EAOP,MAAM,eAAe,CAAC;AAqOvB,eAAO,MAAM,YAAY,EAAE,MAmO1B,CAAC"}
@@ -220,13 +220,32 @@ export const healthAction = {
220
220
  }
221
221
  }
222
222
  const service = new LifeOpsService(runtime);
223
+ const connectorStatus = await service.getHealthConnectorStatus();
223
224
  if (subaction === "status") {
224
- const status = await service.getHealthConnectorStatus();
225
- const text = status.available
226
- ? `Health backend available: ${status.backend}.`
225
+ const text = connectorStatus.available
226
+ ? `Health backend available: ${connectorStatus.backend}.`
227
227
  : "No health backend available. Set ELIZA_HEALTHKIT_CLI_PATH or ELIZA_GOOGLE_FIT_ACCESS_TOKEN.";
228
228
  await callback?.({ text, source: "action", action: "HEALTH" });
229
- return { text, success: true, data: { subaction, status } };
229
+ return {
230
+ text,
231
+ success: true,
232
+ data: { subaction, status: connectorStatus },
233
+ };
234
+ }
235
+ // Graceful degradation: if no HealthKit/GoogleFit backend is configured,
236
+ // surface a clear message instead of letting the health bridge throw.
237
+ if (!connectorStatus.available) {
238
+ const text = "I don't have a health data source connected yet. To share daily summaries, trends, or per-metric details, connect Apple Health (ELIZA_HEALTHKIT_CLI_PATH) or Google Fit (ELIZA_GOOGLE_FIT_ACCESS_TOKEN) and I'll pick it up.";
239
+ await callback?.({ text, source: "action", action: "HEALTH" });
240
+ return {
241
+ text,
242
+ success: true,
243
+ data: {
244
+ subaction,
245
+ status: connectorStatus,
246
+ degraded: "no-backend",
247
+ },
248
+ };
230
249
  }
231
250
  if (subaction === "trend") {
232
251
  const days = params.days && params.days > 0
@@ -1 +1 @@
1
- {"version":3,"file":"life.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/life.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAMP,MAAM,eAAe,CAAC;AAm4EvB,eAAO,MAAM,UAAU,EAAE,MAAM,GAAG;IAChC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAisD1C,CAAC"}
1
+ {"version":3,"file":"life.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/life.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAMP,MAAM,eAAe,CAAC;AA44EvB,eAAO,MAAM,UAAU,EAAE,MAAM,GAAG;IAChC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAisD1C,CAAC"}
@@ -696,6 +696,9 @@ async function resolveOccurrenceWithIntentFallback(args) {
696
696
  return resolveOccurrence(args.service, fallbackTarget, args.domain);
697
697
  }
698
698
  function summarizeCadence(cadence) {
699
+ const cadenceWindows = Array.isArray(cadence.windows)
700
+ ? (cadence.windows).filter((window) => typeof window === "string" && window.trim().length > 0)
701
+ : [];
699
702
  switch (cadence.kind) {
700
703
  case "once": {
701
704
  const dueAt = new Date(cadence.dueAt);
@@ -711,14 +714,18 @@ function summarizeCadence(cadence) {
711
714
  })}`;
712
715
  }
713
716
  case "daily":
714
- return `every day in ${cadence.windows.join(", ")}`;
717
+ return cadenceWindows.length > 0
718
+ ? `every day in ${cadenceWindows.join(", ")}`
719
+ : "every day";
715
720
  case "times_per_day":
716
721
  return cadence.slots
717
722
  .map((slot) => slot.label?.trim() || `${slot.minuteOfDay}`)
718
723
  .filter(Boolean)
719
724
  .join(" and ");
720
725
  case "interval":
721
- return `every ${cadence.everyMinutes} minutes in ${cadence.windows.join(", ")}`;
726
+ return cadenceWindows.length > 0
727
+ ? `every ${cadence.everyMinutes} minutes in ${cadenceWindows.join(", ")}`
728
+ : `every ${cadence.everyMinutes} minutes`;
722
729
  case "weekly":
723
730
  return `weekly on ${cadence.weekdays
724
731
  .map((weekday) => [
@@ -1 +1 @@
1
- {"version":3,"file":"password-manager.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/password-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAyGvB,eAAO,MAAM,qBAAqB,EAAE,MAgJnC,CAAC"}
1
+ {"version":3,"file":"password-manager.d.ts","sourceRoot":"","sources":["../../../../../../../apps/app-lifeops/src/actions/password-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAMZ,MAAM,eAAe,CAAC;AAwJvB,eAAO,MAAM,qBAAqB,EAAE,MAwJnC,CAAC"}