@elizaos/agent 2.0.0-alpha.144 → 2.0.0-alpha.151
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/apps/app-lifeops/src/actions/inbox-digest.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-digest.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-digest.js +1 -0
- package/apps/app-lifeops/src/actions/inbox-respond.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-respond.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-respond.js +1 -0
- package/apps/app-lifeops/src/actions/inbox-triage.d.ts +2 -0
- package/apps/app-lifeops/src/actions/inbox-triage.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox-triage.js +1 -0
- package/apps/app-lifeops/src/actions/inbox.d.ts +3 -0
- package/apps/app-lifeops/src/actions/inbox.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/inbox.js +856 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.d.ts +3 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.d.ts.map +1 -0
- package/apps/app-lifeops/src/actions/update-owner-profile.js +131 -0
- package/apps/app-lifeops/src/inbox/channel-deep-links.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/config.d.ts.map +1 -0
- package/{packages/agent → apps/app-lifeops}/src/inbox/config.js +1 -1
- package/apps/app-lifeops/src/inbox/message-fetcher.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/reflection.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/repository.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/triage-classifier.d.ts.map +1 -0
- package/apps/app-lifeops/src/inbox/types.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/index.d.ts +25 -0
- package/apps/app-lifeops/src/lifeops/index.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/index.js +24 -0
- package/apps/app-lifeops/src/lifeops/screen-context.d.ts +52 -0
- package/apps/app-lifeops/src/lifeops/screen-context.d.ts.map +1 -0
- package/apps/app-lifeops/src/lifeops/screen-context.js +332 -0
- package/apps/app-lifeops/src/plugin.d.ts +3 -0
- package/apps/app-lifeops/src/plugin.d.ts.map +1 -1
- package/apps/app-lifeops/src/plugin.js +16 -3
- package/apps/app-lifeops/src/providers/inbox-triage.d.ts +3 -0
- package/apps/app-lifeops/src/providers/inbox-triage.d.ts.map +1 -0
- package/apps/app-lifeops/src/providers/inbox-triage.js +89 -0
- package/package.json +6 -4
- package/packages/agent/src/actions/calendar.d.ts +1 -18
- package/packages/agent/src/actions/calendar.d.ts.map +1 -1
- package/packages/agent/src/actions/calendar.js +1 -3143
- package/packages/agent/src/actions/check-balance.d.ts +17 -0
- package/packages/agent/src/actions/check-balance.d.ts.map +1 -0
- package/packages/agent/src/actions/check-balance.js +167 -0
- package/packages/agent/src/actions/connector-resolver.d.ts +75 -0
- package/packages/agent/src/actions/connector-resolver.d.ts.map +1 -0
- package/packages/agent/src/actions/connector-resolver.js +245 -0
- package/packages/agent/src/actions/context-signal-lexicon.d.ts +1 -1
- package/packages/agent/src/actions/context-signal-lexicon.d.ts.map +1 -1
- package/packages/agent/src/actions/context-signal-lexicon.js +6 -0
- package/packages/agent/src/actions/eject-plugin.d.ts +3 -0
- package/packages/agent/src/actions/eject-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/eject-plugin.js +48 -0
- package/packages/agent/src/actions/execute-trade.d.ts +17 -0
- package/packages/agent/src/actions/execute-trade.d.ts.map +1 -0
- package/packages/agent/src/actions/execute-trade.js +299 -0
- package/packages/agent/src/actions/get-self-status.d.ts +13 -0
- package/packages/agent/src/actions/get-self-status.d.ts.map +1 -0
- package/packages/agent/src/actions/get-self-status.js +66 -0
- package/packages/agent/src/actions/gmail.d.ts +1 -32
- package/packages/agent/src/actions/gmail.d.ts.map +1 -1
- package/packages/agent/src/actions/gmail.js +1 -1734
- package/packages/agent/src/actions/inbox-digest.d.ts +1 -1
- package/packages/agent/src/actions/inbox-digest.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-digest.js +1 -1
- package/packages/agent/src/actions/inbox-respond.d.ts +1 -1
- package/packages/agent/src/actions/inbox-respond.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-respond.js +1 -1
- package/packages/agent/src/actions/inbox-triage.d.ts +1 -1
- package/packages/agent/src/actions/inbox-triage.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox-triage.js +1 -1
- package/packages/agent/src/actions/inbox.d.ts +1 -2
- package/packages/agent/src/actions/inbox.d.ts.map +1 -1
- package/packages/agent/src/actions/inbox.js +1 -856
- package/packages/agent/src/actions/index.d.ts +13 -0
- package/packages/agent/src/actions/index.d.ts.map +1 -1
- package/packages/agent/src/actions/index.js +13 -0
- package/packages/agent/src/actions/install-plugin.d.ts +3 -0
- package/packages/agent/src/actions/install-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/install-plugin.js +65 -0
- package/packages/agent/src/actions/life-goal-extractor.d.ts +1 -68
- package/packages/agent/src/actions/life-goal-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-goal-extractor.js +1 -354
- package/packages/agent/src/actions/life-param-extractor.d.ts +1 -77
- package/packages/agent/src/actions/life-param-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-param-extractor.js +1 -423
- package/packages/agent/src/actions/life-recent-context.d.ts +1 -8
- package/packages/agent/src/actions/life-recent-context.d.ts.map +1 -1
- package/packages/agent/src/actions/life-recent-context.js +1 -84
- package/packages/agent/src/actions/life-update-extractor.d.ts +1 -26
- package/packages/agent/src/actions/life-update-extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life-update-extractor.js +1 -195
- package/packages/agent/src/actions/life.d.ts +1 -8
- package/packages/agent/src/actions/life.d.ts.map +1 -1
- package/packages/agent/src/actions/life.extractor.d.ts +1 -17
- package/packages/agent/src/actions/life.extractor.d.ts.map +1 -1
- package/packages/agent/src/actions/life.extractor.js +1 -264
- package/packages/agent/src/actions/life.js +1 -3379
- package/packages/agent/src/actions/lifeops-extraction-config.d.ts +1 -15
- package/packages/agent/src/actions/lifeops-extraction-config.d.ts.map +1 -1
- package/packages/agent/src/actions/lifeops-extraction-config.js +1 -25
- package/packages/agent/src/actions/lifeops-google-helpers.d.ts +1 -61
- package/packages/agent/src/actions/lifeops-google-helpers.d.ts.map +1 -1
- package/packages/agent/src/actions/lifeops-google-helpers.js +1 -607
- package/packages/agent/src/actions/list-ejected.d.ts +3 -0
- package/packages/agent/src/actions/list-ejected.d.ts.map +1 -0
- package/packages/agent/src/actions/list-ejected.js +35 -0
- package/packages/agent/src/actions/log-level.d.ts +3 -0
- package/packages/agent/src/actions/log-level.d.ts.map +1 -0
- package/packages/agent/src/actions/log-level.js +125 -0
- package/packages/agent/src/actions/manage-tasks.d.ts.map +1 -1
- package/packages/agent/src/actions/manage-tasks.js +51 -15
- package/packages/agent/src/actions/media.d.ts +21 -0
- package/packages/agent/src/actions/media.d.ts.map +1 -0
- package/packages/agent/src/actions/media.js +384 -0
- package/packages/agent/src/actions/read-messages.d.ts +14 -0
- package/packages/agent/src/actions/read-messages.d.ts.map +1 -0
- package/packages/agent/src/actions/read-messages.js +228 -0
- package/packages/agent/src/actions/reinject-plugin.d.ts +3 -0
- package/packages/agent/src/actions/reinject-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/reinject-plugin.js +47 -0
- package/packages/agent/src/actions/send-message.d.ts +0 -7
- package/packages/agent/src/actions/send-message.d.ts.map +1 -1
- package/packages/agent/src/actions/send-message.js +170 -49
- package/packages/agent/src/actions/sync-plugin.d.ts +3 -0
- package/packages/agent/src/actions/sync-plugin.d.ts.map +1 -0
- package/packages/agent/src/actions/sync-plugin.js +47 -0
- package/packages/agent/src/actions/timezone-normalization.d.ts +1 -2
- package/packages/agent/src/actions/timezone-normalization.d.ts.map +1 -1
- package/packages/agent/src/actions/timezone-normalization.js +1 -107
- package/packages/agent/src/actions/transfer-token.d.ts +17 -0
- package/packages/agent/src/actions/transfer-token.d.ts.map +1 -0
- package/packages/agent/src/actions/transfer-token.js +470 -0
- package/packages/agent/src/actions/update-owner-profile.d.ts +1 -2
- package/packages/agent/src/actions/update-owner-profile.d.ts.map +1 -1
- package/packages/agent/src/actions/update-owner-profile.js +1 -131
- package/packages/agent/src/actions/wallet-action-shared.d.ts +15 -0
- package/packages/agent/src/actions/wallet-action-shared.d.ts.map +1 -0
- package/packages/agent/src/actions/wallet-action-shared.js +24 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts.map +1 -1
- package/packages/agent/src/api/agent-admin-routes.js +1 -1
- package/packages/agent/src/api/binance-skill-helpers.d.ts.map +1 -1
- package/packages/agent/src/api/binance-skill-helpers.js +8 -3
- package/packages/agent/src/api/chat-routes.d.ts.map +1 -1
- package/packages/agent/src/api/chat-routes.js +20 -5
- package/packages/agent/src/api/coding-agents-auth-sanitize.d.ts +1 -22
- package/packages/agent/src/api/coding-agents-auth-sanitize.d.ts.map +1 -1
- package/packages/agent/src/api/coding-agents-auth-sanitize.js +1 -39
- package/packages/agent/src/api/coding-agents-preflight-normalize.d.ts +1 -28
- package/packages/agent/src/api/coding-agents-preflight-normalize.d.ts.map +1 -1
- package/packages/agent/src/api/coding-agents-preflight-normalize.js +1 -45
- package/packages/agent/src/api/coordinator-types.d.ts +1 -46
- package/packages/agent/src/api/coordinator-types.d.ts.map +1 -1
- package/packages/agent/src/api/coordinator-types.js +1 -1
- package/packages/agent/src/api/coordinator-wiring.d.ts +1 -45
- package/packages/agent/src/api/coordinator-wiring.d.ts.map +1 -1
- package/packages/agent/src/api/coordinator-wiring.js +1 -108
- package/packages/agent/src/api/index.d.ts +1 -1
- package/packages/agent/src/api/index.d.ts.map +1 -1
- package/packages/agent/src/api/index.js +1 -1
- package/packages/agent/src/api/lifeops-browser-packaging.d.ts +1 -15
- package/packages/agent/src/api/lifeops-browser-packaging.d.ts.map +1 -1
- package/packages/agent/src/api/lifeops-browser-packaging.js +1 -305
- package/packages/agent/src/api/lifeops-routes.d.ts +1 -19
- package/packages/agent/src/api/lifeops-routes.d.ts.map +1 -1
- package/packages/agent/src/api/lifeops-routes.js +1 -1173
- package/packages/agent/src/api/server.d.ts.map +1 -1
- package/packages/agent/src/api/server.js +6 -6
- package/packages/agent/src/api/task-agent-message-routing.d.ts +1 -9
- package/packages/agent/src/api/task-agent-message-routing.d.ts.map +1 -1
- package/packages/agent/src/api/task-agent-message-routing.js +1 -62
- package/packages/agent/src/api/website-blocker-routes.d.ts +1 -6
- package/packages/agent/src/api/website-blocker-routes.d.ts.map +1 -1
- package/packages/agent/src/api/website-blocker-routes.js +1 -174
- package/packages/agent/src/config/types.agent-defaults.d.ts +1 -1
- package/packages/agent/src/config/types.agent-defaults.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-eval-client.d.ts +1 -38
- package/packages/agent/src/evals/coordinator-eval-client.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-eval-client.js +1 -138
- package/packages/agent/src/evals/coordinator-live-runner.d.ts +1 -56
- package/packages/agent/src/evals/coordinator-live-runner.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-live-runner.js +1 -546
- package/packages/agent/src/evals/coordinator-preflight.d.ts +1 -31
- package/packages/agent/src/evals/coordinator-preflight.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-preflight.js +1 -296
- package/packages/agent/src/evals/coordinator-scenarios.d.ts +1 -23
- package/packages/agent/src/evals/coordinator-scenarios.d.ts.map +1 -1
- package/packages/agent/src/evals/coordinator-scenarios.js +1 -1141
- package/packages/agent/src/lifeops/app-state.d.ts +1 -10
- package/packages/agent/src/lifeops/app-state.d.ts.map +1 -1
- package/packages/agent/src/lifeops/app-state.js +1 -32
- package/packages/agent/src/lifeops/apple-reminders.d.ts +1 -57
- package/packages/agent/src/lifeops/apple-reminders.d.ts.map +1 -1
- package/packages/agent/src/lifeops/apple-reminders.js +1 -325
- package/packages/agent/src/lifeops/defaults.d.ts +1 -23
- package/packages/agent/src/lifeops/defaults.d.ts.map +1 -1
- package/packages/agent/src/lifeops/defaults.js +1 -205
- package/packages/agent/src/lifeops/engine.d.ts +1 -7
- package/packages/agent/src/lifeops/engine.d.ts.map +1 -1
- package/packages/agent/src/lifeops/engine.js +1 -389
- package/packages/agent/src/lifeops/goal-grounding.d.ts +1 -53
- package/packages/agent/src/lifeops/goal-grounding.d.ts.map +1 -1
- package/packages/agent/src/lifeops/goal-grounding.js +1 -147
- package/packages/agent/src/lifeops/goal-semantic-evaluator.d.ts +1 -11
- package/packages/agent/src/lifeops/goal-semantic-evaluator.d.ts.map +1 -1
- package/packages/agent/src/lifeops/goal-semantic-evaluator.js +1 -154
- package/packages/agent/src/lifeops/google-api-error.d.ts +1 -6
- package/packages/agent/src/lifeops/google-api-error.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-api-error.js +1 -35
- package/packages/agent/src/lifeops/google-calendar.d.ts +1 -52
- package/packages/agent/src/lifeops/google-calendar.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-calendar.js +1 -268
- package/packages/agent/src/lifeops/google-connector-gateway.d.ts +1 -18
- package/packages/agent/src/lifeops/google-connector-gateway.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-connector-gateway.js +1 -65
- package/packages/agent/src/lifeops/google-fetch.d.ts +1 -10
- package/packages/agent/src/lifeops/google-fetch.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-fetch.js +1 -85
- package/packages/agent/src/lifeops/google-gmail.d.ts +1 -53
- package/packages/agent/src/lifeops/google-gmail.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-gmail.js +1 -471
- package/packages/agent/src/lifeops/google-managed-client.d.ts +1 -126
- package/packages/agent/src/lifeops/google-managed-client.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-managed-client.js +1 -294
- package/packages/agent/src/lifeops/google-oauth.d.ts +1 -60
- package/packages/agent/src/lifeops/google-oauth.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-oauth.js +1 -494
- package/packages/agent/src/lifeops/google-scopes.d.ts +1 -12
- package/packages/agent/src/lifeops/google-scopes.d.ts.map +1 -1
- package/packages/agent/src/lifeops/google-scopes.js +1 -96
- package/packages/agent/src/lifeops/index.d.ts +1 -2
- package/packages/agent/src/lifeops/index.d.ts.map +1 -1
- package/packages/agent/src/lifeops/index.js +1 -2
- package/packages/agent/src/lifeops/owner-profile.d.ts +1 -14
- package/packages/agent/src/lifeops/owner-profile.d.ts.map +1 -1
- package/packages/agent/src/lifeops/owner-profile.js +1 -194
- package/packages/agent/src/lifeops/repository.d.ts +1 -208
- package/packages/agent/src/lifeops/repository.d.ts.map +1 -1
- package/packages/agent/src/lifeops/repository.js +1 -3187
- package/packages/agent/src/lifeops/runtime.d.ts +1 -13
- package/packages/agent/src/lifeops/runtime.d.ts.map +1 -1
- package/packages/agent/src/lifeops/runtime.js +1 -120
- package/packages/agent/src/lifeops/screen-context.d.ts +1 -51
- package/packages/agent/src/lifeops/screen-context.d.ts.map +1 -1
- package/packages/agent/src/lifeops/screen-context.js +1 -332
- package/packages/agent/src/lifeops/seed-routines.d.ts +1 -19
- package/packages/agent/src/lifeops/seed-routines.d.ts.map +1 -1
- package/packages/agent/src/lifeops/seed-routines.js +1 -111
- package/packages/agent/src/lifeops/service.d.ts +1 -274
- package/packages/agent/src/lifeops/service.d.ts.map +1 -1
- package/packages/agent/src/lifeops/service.js +1 -9260
- package/packages/agent/src/lifeops/sql.d.ts +1 -30
- package/packages/agent/src/lifeops/sql.d.ts.map +1 -1
- package/packages/agent/src/lifeops/sql.js +1 -247
- package/packages/agent/src/lifeops/time.d.ts +1 -16
- package/packages/agent/src/lifeops/time.d.ts.map +1 -1
- package/packages/agent/src/lifeops/time.js +1 -132
- package/packages/agent/src/lifeops/twilio.d.ts +1 -24
- package/packages/agent/src/lifeops/twilio.d.ts.map +1 -1
- package/packages/agent/src/lifeops/twilio.js +1 -157
- package/packages/agent/src/lifeops/x-poster.d.ts +1 -18
- package/packages/agent/src/lifeops/x-poster.d.ts.map +1 -1
- package/packages/agent/src/lifeops/x-poster.js +1 -148
- package/packages/agent/src/providers/inbox-triage.d.ts +1 -2
- package/packages/agent/src/providers/inbox-triage.d.ts.map +1 -1
- package/packages/agent/src/providers/inbox-triage.js +1 -89
- package/packages/agent/src/providers/index.d.ts +4 -1
- package/packages/agent/src/providers/index.d.ts.map +1 -1
- package/packages/agent/src/providers/index.js +4 -1
- package/packages/agent/src/providers/lifeops.d.ts +1 -2
- package/packages/agent/src/providers/lifeops.d.ts.map +1 -1
- package/packages/agent/src/providers/lifeops.js +1 -157
- package/packages/agent/src/providers/local-models.d.ts +118 -0
- package/packages/agent/src/providers/local-models.d.ts.map +1 -0
- package/packages/agent/src/providers/local-models.js +427 -0
- package/packages/agent/src/providers/media-provider.d.ts +192 -0
- package/packages/agent/src/providers/media-provider.d.ts.map +1 -0
- package/packages/agent/src/providers/media-provider.js +1088 -0
- package/packages/agent/src/providers/self-status.d.ts +4 -0
- package/packages/agent/src/providers/self-status.d.ts.map +1 -0
- package/packages/agent/src/providers/self-status.js +12 -0
- package/packages/agent/src/providers/tasks.d.ts.map +1 -1
- package/packages/agent/src/providers/tasks.js +7 -7
- package/packages/agent/src/runtime/core-plugins.js +1 -1
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -1
- package/packages/agent/src/runtime/eliza-plugin.js +1 -7
- package/packages/agent/src/runtime/eliza.js +2 -2
- package/packages/agent/src/runtime/plugin-collector.js +3 -3
- package/packages/agent/src/runtime/plugin-lifecycle.d.ts.map +1 -1
- package/packages/agent/src/runtime/plugin-lifecycle.js +3 -13
- package/packages/agent/src/runtime/trajectory-internals.d.ts.map +1 -1
- package/packages/agent/src/runtime/trajectory-internals.js +1 -3
- package/packages/agent/src/services/built-in-app-routes/hyperscape.d.ts.map +1 -1
- package/packages/agent/src/services/coding-task-executor.d.ts +3 -3
- package/packages/agent/src/services/coding-task-executor.js +3 -3
- package/packages/shared/src/awareness/index.d.ts +2 -0
- package/packages/shared/src/awareness/index.d.ts.map +1 -0
- package/packages/shared/src/awareness/index.js +1 -0
- package/packages/shared/src/awareness/registry.d.ts +27 -0
- package/packages/shared/src/awareness/registry.d.ts.map +1 -0
- package/packages/shared/src/awareness/registry.js +161 -0
- package/packages/shared/src/i18n/generated/validation-keyword-data.d.ts +24 -0
- package/packages/shared/src/i18n/generated/validation-keyword-data.d.ts.map +1 -1
- package/packages/shared/src/i18n/generated/validation-keyword-data.js +24 -0
- package/packages/shared/src/runtime-env.d.ts.map +1 -1
- package/packages/shared/src/runtime-env.js +5 -1
- package/packages/typescript/src/generated/action-docs.d.ts +135 -0
- package/packages/typescript/src/generated/action-docs.d.ts.map +1 -1
- package/packages/typescript/src/generated/action-docs.js +237 -0
- package/packages/typescript/src/i18n/generated/validation-keyword-data.d.ts +24 -0
- package/packages/typescript/src/i18n/generated/validation-keyword-data.d.ts.map +1 -1
- package/packages/typescript/src/i18n/generated/validation-keyword-data.js +24 -0
- package/packages/typescript/src/index.node.d.ts +2 -2
- package/packages/typescript/src/index.node.d.ts.map +1 -1
- package/packages/typescript/src/index.node.js +4 -3
- package/packages/typescript/src/plugin-lifecycle.d.ts.map +1 -1
- package/packages/typescript/src/plugin-lifecycle.js +42 -3
- package/packages/typescript/src/services/message.d.ts.map +1 -1
- package/packages/typescript/src/services/message.js +32 -0
- package/apps/app-training/src/core/cli.d.ts +0 -11
- package/apps/app-training/src/core/cli.d.ts.map +0 -1
- package/apps/app-training/src/core/cli.js +0 -302
- package/apps/app-training/src/core/context-audit.d.ts +0 -51
- package/apps/app-training/src/core/context-audit.d.ts.map +0 -1
- package/apps/app-training/src/core/context-audit.js +0 -141
- package/apps/app-training/src/core/context-catalog.d.ts +0 -47
- package/apps/app-training/src/core/context-catalog.d.ts.map +0 -1
- package/apps/app-training/src/core/context-catalog.js +0 -259
- package/apps/app-training/src/core/context-types.d.ts +0 -3
- package/apps/app-training/src/core/context-types.d.ts.map +0 -1
- package/apps/app-training/src/core/context-types.js +0 -11
- package/apps/app-training/src/core/dataset-generator.d.ts +0 -135
- package/apps/app-training/src/core/dataset-generator.d.ts.map +0 -1
- package/apps/app-training/src/core/dataset-generator.js +0 -703
- package/apps/app-training/src/core/replay-validator.d.ts +0 -96
- package/apps/app-training/src/core/replay-validator.d.ts.map +0 -1
- package/apps/app-training/src/core/replay-validator.js +0 -265
- package/apps/app-training/src/core/roleplay-executor.d.ts +0 -123
- package/apps/app-training/src/core/roleplay-executor.d.ts.map +0 -1
- package/apps/app-training/src/core/roleplay-executor.js +0 -645
- package/apps/app-training/src/core/roleplay-trajectories.d.ts +0 -54
- package/apps/app-training/src/core/roleplay-trajectories.d.ts.map +0 -1
- package/apps/app-training/src/core/roleplay-trajectories.js +0 -73
- package/apps/app-training/src/core/scenario-blueprints.d.ts +0 -62
- package/apps/app-training/src/core/scenario-blueprints.d.ts.map +0 -1
- package/apps/app-training/src/core/scenario-blueprints.js +0 -790
- package/apps/app-training/src/core/trajectory-task-datasets.d.ts +0 -38
- package/apps/app-training/src/core/trajectory-task-datasets.d.ts.map +0 -1
- package/apps/app-training/src/core/trajectory-task-datasets.js +0 -281
- package/apps/app-training/src/core/vertex-tuning.d.ts +0 -139
- package/apps/app-training/src/core/vertex-tuning.d.ts.map +0 -1
- package/apps/app-training/src/core/vertex-tuning.js +0 -234
- package/packages/agent/src/inbox/channel-deep-links.d.ts.map +0 -1
- package/packages/agent/src/inbox/config.d.ts.map +0 -1
- package/packages/agent/src/inbox/message-fetcher.d.ts.map +0 -1
- package/packages/agent/src/inbox/reflection.d.ts.map +0 -1
- package/packages/agent/src/inbox/repository.d.ts.map +0 -1
- package/packages/agent/src/inbox/triage-classifier.d.ts.map +0 -1
- package/packages/agent/src/inbox/types.d.ts.map +0 -1
- package/packages/agent/src/training/cli.d.ts +0 -2
- package/packages/agent/src/training/cli.d.ts.map +0 -1
- package/packages/agent/src/training/cli.js +0 -2
- package/packages/agent/src/training/context-audit.d.ts +0 -2
- package/packages/agent/src/training/context-audit.d.ts.map +0 -1
- package/packages/agent/src/training/context-audit.js +0 -2
- package/packages/agent/src/training/context-catalog.d.ts +0 -2
- package/packages/agent/src/training/context-catalog.d.ts.map +0 -1
- package/packages/agent/src/training/context-catalog.js +0 -2
- package/packages/agent/src/training/context-types.d.ts +0 -2
- package/packages/agent/src/training/context-types.d.ts.map +0 -1
- package/packages/agent/src/training/context-types.js +0 -2
- package/packages/agent/src/training/dataset-generator.d.ts +0 -2
- package/packages/agent/src/training/dataset-generator.d.ts.map +0 -1
- package/packages/agent/src/training/dataset-generator.js +0 -2
- package/packages/agent/src/training/replay-validator.d.ts +0 -2
- package/packages/agent/src/training/replay-validator.d.ts.map +0 -1
- package/packages/agent/src/training/replay-validator.js +0 -2
- package/packages/agent/src/training/roleplay-executor.d.ts +0 -2
- package/packages/agent/src/training/roleplay-executor.d.ts.map +0 -1
- package/packages/agent/src/training/roleplay-executor.js +0 -2
- package/packages/agent/src/training/roleplay-trajectories.d.ts +0 -2
- package/packages/agent/src/training/roleplay-trajectories.d.ts.map +0 -1
- package/packages/agent/src/training/roleplay-trajectories.js +0 -2
- package/packages/agent/src/training/scenario-blueprints.d.ts +0 -2
- package/packages/agent/src/training/scenario-blueprints.d.ts.map +0 -1
- package/packages/agent/src/training/scenario-blueprints.js +0 -2
- package/packages/agent/src/training/trajectory-task-datasets.d.ts +0 -2
- package/packages/agent/src/training/trajectory-task-datasets.d.ts.map +0 -1
- package/packages/agent/src/training/trajectory-task-datasets.js +0 -2
- package/packages/agent/src/training/vertex-tuning.d.ts +0 -2
- package/packages/agent/src/training/vertex-tuning.d.ts.map +0 -1
- package/packages/agent/src/training/vertex-tuning.js +0 -2
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.d.ts +0 -41
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/coding-task-handlers.js +0 -443
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.d.ts +0 -34
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/coding-task-helpers.js +0 -171
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/eval-metadata.js +0 -55
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/finalize-workspace.js +0 -214
- package/packages/typescript/src/features/orchestrator/actions/list-agents.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/actions/list-agents.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/list-agents.js +0 -174
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/manage-issues.js +0 -428
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/provision-workspace.js +0 -189
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/send-to-agent.js +0 -265
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/spawn-agent.js +0 -356
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/start-coding-task.js +0 -270
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.d.ts +0 -12
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/stop-agent.js +0 -192
- package/packages/typescript/src/features/orchestrator/actions/task-control.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-control.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-control.js +0 -217
- package/packages/typescript/src/features/orchestrator/actions/task-history.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-history.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-history.js +0 -323
- package/packages/typescript/src/features/orchestrator/actions/task-share.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/actions/task-share.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-share.js +0 -168
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.d.ts +0 -11
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/actions/task-thread-target.js +0 -68
- package/packages/typescript/src/features/orchestrator/api/agent-routes.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/api/agent-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/agent-routes.js +0 -654
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/coordinator-routes.js +0 -403
- package/packages/typescript/src/features/orchestrator/api/hook-routes.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/api/hook-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/hook-routes.js +0 -164
- package/packages/typescript/src/features/orchestrator/api/issue-routes.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/api/issue-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/issue-routes.js +0 -132
- package/packages/typescript/src/features/orchestrator/api/routes.d.ts +0 -37
- package/packages/typescript/src/features/orchestrator/api/routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/routes.js +0 -96
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/api/workspace-routes.js +0 -149
- package/packages/typescript/src/features/orchestrator/base-plugin.d.ts +0 -19
- package/packages/typescript/src/features/orchestrator/base-plugin.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/base-plugin.js +0 -75
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.d.ts +0 -101
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/claude-jsonl-completion-watcher.js +0 -310
- package/packages/typescript/src/features/orchestrator/index.d.ts +0 -33
- package/packages/typescript/src/features/orchestrator/index.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/index.js +0 -30
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.d.ts +0 -15
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/patch-agent-orchestrator-plugin.js +0 -1449
- package/packages/typescript/src/features/orchestrator/providers/action-examples.d.ts +0 -14
- package/packages/typescript/src/features/orchestrator/providers/action-examples.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/providers/action-examples.js +0 -151
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/providers/active-workspace-context.js +0 -142
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.d.ts +0 -6
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-credentials.js +0 -91
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.d.ts +0 -30
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-metrics.js +0 -54
- package/packages/typescript/src/features/orchestrator/services/agent-selection.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/agent-selection.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/agent-selection.js +0 -70
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.d.ts +0 -61
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/ansi-utils.js +0 -252
- package/packages/typescript/src/features/orchestrator/services/config-env.d.ts +0 -13
- package/packages/typescript/src/features/orchestrator/services/config-env.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/config-env.js +0 -37
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.d.ts +0 -50
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/coordinator-event-normalizer.js +0 -184
- package/packages/typescript/src/features/orchestrator/services/debug-capture.d.ts +0 -38
- package/packages/typescript/src/features/orchestrator/services/debug-capture.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/debug-capture.js +0 -113
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.d.ts +0 -30
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-auto-response.js +0 -146
- package/packages/typescript/src/features/orchestrator/services/pty-init.d.ts +0 -54
- package/packages/typescript/src/features/orchestrator/services/pty-init.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-init.js +0 -315
- package/packages/typescript/src/features/orchestrator/services/pty-service.d.ts +0 -175
- package/packages/typescript/src/features/orchestrator/services/pty-service.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-service.js +0 -1469
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.d.ts +0 -49
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-session-io.js +0 -180
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-spawn.js +0 -280
- package/packages/typescript/src/features/orchestrator/services/pty-types.d.ts +0 -80
- package/packages/typescript/src/features/orchestrator/services/pty-types.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/pty-types.js +0 -51
- package/packages/typescript/src/features/orchestrator/services/repo-input.d.ts +0 -16
- package/packages/typescript/src/features/orchestrator/services/repo-input.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/repo-input.js +0 -88
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.d.ts +0 -69
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/stall-classifier.js +0 -446
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.d.ts +0 -97
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator-prompts.js +0 -342
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.d.ts +0 -421
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-coordinator.js +0 -2356
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.d.ts +0 -52
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-decision-loop.js +0 -1538
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.d.ts +0 -49
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-event-triage.js +0 -171
- package/packages/typescript/src/features/orchestrator/services/swarm-history.d.ts +0 -27
- package/packages/typescript/src/features/orchestrator/services/swarm-history.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-history.js +0 -148
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.d.ts +0 -22
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/swarm-idle-watchdog.js +0 -265
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.d.ts +0 -8
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-acceptance.js +0 -114
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.d.ts +0 -68
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-agent-auth.js +0 -559
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.d.ts +0 -82
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-agent-frameworks.js +0 -738
- package/packages/typescript/src/features/orchestrator/services/task-kind.d.ts +0 -3
- package/packages/typescript/src/features/orchestrator/services/task-kind.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-kind.js +0 -40
- package/packages/typescript/src/features/orchestrator/services/task-policy.d.ts +0 -17
- package/packages/typescript/src/features/orchestrator/services/task-policy.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-policy.js +0 -226
- package/packages/typescript/src/features/orchestrator/services/task-registry.d.ts +0 -550
- package/packages/typescript/src/features/orchestrator/services/task-registry.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-registry.js +0 -2182
- package/packages/typescript/src/features/orchestrator/services/task-share.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/services/task-share.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-share.js +0 -159
- package/packages/typescript/src/features/orchestrator/services/task-validation.d.ts +0 -69
- package/packages/typescript/src/features/orchestrator/services/task-validation.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-validation.js +0 -587
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.d.ts +0 -5
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/task-verifier-runner.js +0 -372
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.d.ts +0 -73
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/trajectory-context.js +0 -64
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.d.ts +0 -53
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/trajectory-feedback.js +0 -260
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.d.ts +0 -28
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-git-ops.js +0 -105
- package/packages/typescript/src/features/orchestrator/services/workspace-github.d.ts +0 -58
- package/packages/typescript/src/features/orchestrator/services/workspace-github.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-github.js +0 -139
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.d.ts +0 -18
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-lifecycle.js +0 -86
- package/packages/typescript/src/features/orchestrator/services/workspace-service.d.ts +0 -118
- package/packages/typescript/src/features/orchestrator/services/workspace-service.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-service.js +0 -533
- package/packages/typescript/src/features/orchestrator/services/workspace-types.d.ts +0 -81
- package/packages/typescript/src/features/orchestrator/services/workspace-types.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/services/workspace-types.js +0 -8
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.d.ts +0 -38
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.d.ts.map +0 -1
- package/packages/typescript/src/features/orchestrator/task-progress-streamer.js +0 -293
- /package/{packages/agent → apps/app-lifeops}/src/inbox/channel-deep-links.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/channel-deep-links.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/config.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/message-fetcher.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/message-fetcher.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/reflection.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/reflection.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/repository.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/repository.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/triage-classifier.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/triage-classifier.js +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/types.d.ts +0 -0
- /package/{packages/agent → apps/app-lifeops}/src/inbox/types.js +0 -0
|
@@ -1,3379 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { getValidationKeywordTerms, textIncludesKeywordTerm, } from "@elizaos/shared/validation-keywords";
|
|
3
|
-
import { buildNativeAppleReminderMetadata, } from "../lifeops/apple-reminders.js";
|
|
4
|
-
import { resolveDefaultTimeZone, resolveDefaultWindowPolicy, } from "../lifeops/defaults.js";
|
|
5
|
-
import { LifeOpsService, LifeOpsServiceError } from "../lifeops/service.js";
|
|
6
|
-
import { addDaysToLocalDate, buildUtcDateFromLocalParts, getZonedDateParts, } from "../lifeops/time.js";
|
|
7
|
-
import { gmailAction } from "./gmail.js";
|
|
8
|
-
import { renderGroundedActionReply } from "./grounded-action-reply.js";
|
|
9
|
-
import { extractLifeOperationWithLlm, } from "./life.extractor.js";
|
|
10
|
-
import { extractGoalCreatePlanWithLlm, extractGoalUpdatePlanWithLlm, mergeGoalMetadataWithGrounding, } from "./life-goal-extractor.js";
|
|
11
|
-
import { extractReminderIntensityWithLlm, extractTaskCreatePlanWithLlm, } from "./life-param-extractor.js";
|
|
12
|
-
import { recentConversationTexts } from "./life-recent-context.js";
|
|
13
|
-
import { extractUpdateFieldsWithLlm } from "./life-update-extractor.js";
|
|
14
|
-
import { calendarReadUnavailableMessage, dayRange, detailArray, detailBoolean, detailNumber, detailObject, detailString, formatCalendarFeed, formatNextEventContext, formatOverviewForQuery, getGoogleCapabilityStatus, hasLifeOpsAccess, INTERNAL_URL, messageText, toActionData, weekRange, } from "./lifeops-google-helpers.js";
|
|
15
|
-
import { extractExplicitTimeZoneFromText, normalizeExplicitTimeZoneToken, } from "./timezone-normalization.js";
|
|
16
|
-
const LIFE_EMAIL_QUERY_TERMS = getValidationKeywordTerms("contextSignal.gmail.strong", {
|
|
17
|
-
includeAllLocales: true,
|
|
18
|
-
});
|
|
19
|
-
const LIFE_I18N_OPTS = { includeAllLocales: true };
|
|
20
|
-
const LIFE_COMPLETE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_complete.strong", LIFE_I18N_OPTS);
|
|
21
|
-
const LIFE_SKIP_TERMS = getValidationKeywordTerms("contextSignal.lifeops_skip.strong", LIFE_I18N_OPTS);
|
|
22
|
-
const LIFE_SNOOZE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_snooze.strong", LIFE_I18N_OPTS);
|
|
23
|
-
const LIFE_DELETE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_delete.strong", LIFE_I18N_OPTS);
|
|
24
|
-
const LIFE_UPDATE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_update.strong", LIFE_I18N_OPTS);
|
|
25
|
-
const LIFE_OVERVIEW_TERMS = getValidationKeywordTerms("contextSignal.lifeops_overview.strong", LIFE_I18N_OPTS);
|
|
26
|
-
const LIFE_REMINDER_PREF_TERMS = getValidationKeywordTerms("contextSignal.lifeops_reminder_pref.strong", LIFE_I18N_OPTS);
|
|
27
|
-
const LIFE_CALENDAR_TERMS = getValidationKeywordTerms("contextSignal.calendar.strong", LIFE_I18N_OPTS);
|
|
28
|
-
const LIFE_CADENCE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_cadence.strong", LIFE_I18N_OPTS);
|
|
29
|
-
const LIFE_GOAL_TERMS = getValidationKeywordTerms("contextSignal.lifeops_goal.strong", LIFE_I18N_OPTS);
|
|
30
|
-
const LIFE_ESCALATION_TERMS = getValidationKeywordTerms("contextSignal.lifeops_escalation.strong", LIFE_I18N_OPTS);
|
|
31
|
-
const LIFE_PHONE_TERMS = getValidationKeywordTerms("contextSignal.lifeops_phone.strong", LIFE_I18N_OPTS);
|
|
32
|
-
const LIFE_REVIEW_TERMS = getValidationKeywordTerms("contextSignal.lifeops_review.strong", LIFE_I18N_OPTS);
|
|
33
|
-
const LIFE_LIFEOPS_STRONG_TERMS = getValidationKeywordTerms("contextSignal.lifeops.strong", LIFE_I18N_OPTS);
|
|
34
|
-
const LIFE_AFFIRMATIVE_TERMS = getValidationKeywordTerms("contextSignal.affirmative.strong", LIFE_I18N_OPTS);
|
|
35
|
-
const LIFE_NEGATIVE_TERMS = getValidationKeywordTerms("contextSignal.negative.strong", LIFE_I18N_OPTS);
|
|
36
|
-
const LIFE_DRAFT_EDIT_TERMS = getValidationKeywordTerms("contextSignal.draft_edit.strong", LIFE_I18N_OPTS);
|
|
37
|
-
const LIFE_TEMPORAL_NEXT_TERMS = getValidationKeywordTerms("contextSignal.temporal_next.strong", LIFE_I18N_OPTS);
|
|
38
|
-
function textMatchesAnyTerm(text, terms) {
|
|
39
|
-
return terms.some((term) => textIncludesKeywordTerm(text, term));
|
|
40
|
-
}
|
|
41
|
-
const ACTION_TO_OPERATION = {
|
|
42
|
-
create: "create_definition",
|
|
43
|
-
create_goal: "create_goal",
|
|
44
|
-
update: "update_definition",
|
|
45
|
-
update_goal: "update_goal",
|
|
46
|
-
delete: "delete_definition",
|
|
47
|
-
delete_goal: "delete_goal",
|
|
48
|
-
complete: "complete_occurrence",
|
|
49
|
-
skip: "skip_occurrence",
|
|
50
|
-
snooze: "snooze_occurrence",
|
|
51
|
-
review: "review_goal",
|
|
52
|
-
phone: "capture_phone",
|
|
53
|
-
escalation: "configure_escalation",
|
|
54
|
-
reminder_preference: "set_reminder_preference",
|
|
55
|
-
calendar: "query_calendar_today",
|
|
56
|
-
next_event: "query_calendar_next",
|
|
57
|
-
email: "query_email",
|
|
58
|
-
overview: "query_overview",
|
|
59
|
-
};
|
|
60
|
-
// CADENCE_HINT_RE removed — cadence detection uses i18n LIFE_CADENCE_TERMS
|
|
61
|
-
const GENERIC_DERIVED_TITLE_RE = /^(?:new\s+)?(?:habit|routine|task|goal|life goal|thing|item|something|anything|stuff|plan|reminder|todo|to do|achieve|achieve a|achieve an)$/i;
|
|
62
|
-
/** Maximum age (ms) for a deferred draft before it expires. */
|
|
63
|
-
const DRAFT_EXPIRY_MS = 5 * 60 * 1000; // 5 minutes
|
|
64
|
-
/** Maximum conversation turns before a deferred draft expires. */
|
|
65
|
-
const DRAFT_MAX_TURNS = 3;
|
|
66
|
-
function normalizeLifeTimeZoneToken(value) {
|
|
67
|
-
return normalizeExplicitTimeZoneToken(value);
|
|
68
|
-
}
|
|
69
|
-
function extractLifeTimeZoneFromText(value) {
|
|
70
|
-
return extractExplicitTimeZoneFromText(value);
|
|
71
|
-
}
|
|
72
|
-
// ── Intent classifier ─────────────────────────────────
|
|
73
|
-
export function classifyIntent(intent) {
|
|
74
|
-
// All matching is i18n-aware via validation keyword terms.
|
|
75
|
-
// English words are included in the base terms so no regex needed.
|
|
76
|
-
// Reminder preference — check early
|
|
77
|
-
if (textMatchesAnyTerm(intent, LIFE_REMINDER_PREF_TERMS)) {
|
|
78
|
-
return "set_reminder_preference";
|
|
79
|
-
}
|
|
80
|
-
// Update — check before calendar so "edit my workout schedule" doesn't hit calendar
|
|
81
|
-
if (textMatchesAnyTerm(intent, LIFE_UPDATE_TERMS)) {
|
|
82
|
-
if (textMatchesAnyTerm(intent, LIFE_GOAL_TERMS))
|
|
83
|
-
return "update_goal";
|
|
84
|
-
return "update_definition";
|
|
85
|
-
}
|
|
86
|
-
// Escalation config — check before phone capture
|
|
87
|
-
if (textMatchesAnyTerm(intent, LIFE_ESCALATION_TERMS))
|
|
88
|
-
return "configure_escalation";
|
|
89
|
-
// Phone capture
|
|
90
|
-
if (textMatchesAnyTerm(intent, LIFE_PHONE_TERMS))
|
|
91
|
-
return "capture_phone";
|
|
92
|
-
// Review — check before calendar
|
|
93
|
-
if (textMatchesAnyTerm(intent, LIFE_REVIEW_TERMS))
|
|
94
|
-
return "review_goal";
|
|
95
|
-
// Delete — check before calendar
|
|
96
|
-
if (textMatchesAnyTerm(intent, LIFE_DELETE_TERMS)) {
|
|
97
|
-
if (textMatchesAnyTerm(intent, LIFE_GOAL_TERMS))
|
|
98
|
-
return "delete_goal";
|
|
99
|
-
return "delete_definition";
|
|
100
|
-
}
|
|
101
|
-
// Completion
|
|
102
|
-
if (looksLikeCompletionReport(intent))
|
|
103
|
-
return "complete_occurrence";
|
|
104
|
-
// Skip
|
|
105
|
-
if (textMatchesAnyTerm(intent, LIFE_SKIP_TERMS))
|
|
106
|
-
return "skip_occurrence";
|
|
107
|
-
// Snooze
|
|
108
|
-
if (textMatchesAnyTerm(intent, LIFE_SNOOZE_TERMS))
|
|
109
|
-
return "snooze_occurrence";
|
|
110
|
-
// Calendar query — only when not a lifeops create or lifeops item reference
|
|
111
|
-
if (!looksLikeDefinitionCreateIntent(intent) &&
|
|
112
|
-
!looksLikeGoalCreateIntent(intent) &&
|
|
113
|
-
!textMatchesAnyTerm(intent, LIFE_LIFEOPS_STRONG_TERMS) &&
|
|
114
|
-
textMatchesAnyTerm(intent, LIFE_CALENDAR_TERMS)) {
|
|
115
|
-
// Sub-classify: next event vs today/tomorrow/week
|
|
116
|
-
const lower = intent.toLowerCase();
|
|
117
|
-
if (textMatchesAnyTerm(lower, LIFE_TEMPORAL_NEXT_TERMS))
|
|
118
|
-
return "query_calendar_next";
|
|
119
|
-
return "query_calendar_today";
|
|
120
|
-
}
|
|
121
|
-
// Email query
|
|
122
|
-
if (textMatchesAnyTerm(intent, LIFE_EMAIL_QUERY_TERMS))
|
|
123
|
-
return "query_email";
|
|
124
|
-
// Overview
|
|
125
|
-
if (textMatchesAnyTerm(intent, LIFE_OVERVIEW_TERMS))
|
|
126
|
-
return "query_overview";
|
|
127
|
-
// Create definition (has cadence hint)
|
|
128
|
-
if (looksLikeDefinitionCreateIntent(intent))
|
|
129
|
-
return "create_definition";
|
|
130
|
-
// Create goal (goal mention without cadence)
|
|
131
|
-
if (looksLikeGoalCreateIntent(intent))
|
|
132
|
-
return "create_goal";
|
|
133
|
-
// Default: create a task/habit/routine
|
|
134
|
-
return "create_definition";
|
|
135
|
-
}
|
|
136
|
-
async function resolveLifeOperationPlan(args) {
|
|
137
|
-
const { runtime, message, state, intent, explicitOperation } = args;
|
|
138
|
-
if (explicitOperation) {
|
|
139
|
-
return {
|
|
140
|
-
operation: explicitOperation,
|
|
141
|
-
confidence: 1,
|
|
142
|
-
missing: [],
|
|
143
|
-
shouldAct: true,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
const extracted = await extractLifeOperationWithLlm({
|
|
147
|
-
runtime,
|
|
148
|
-
message,
|
|
149
|
-
state,
|
|
150
|
-
intent,
|
|
151
|
-
});
|
|
152
|
-
if (!extracted.shouldAct || !extracted.operation) {
|
|
153
|
-
return {
|
|
154
|
-
operation: extracted.operation,
|
|
155
|
-
confidence: extracted.confidence,
|
|
156
|
-
missing: extracted.missing,
|
|
157
|
-
shouldAct: false,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
operation: extracted.operation,
|
|
162
|
-
confidence: extracted.confidence,
|
|
163
|
-
missing: extracted.missing,
|
|
164
|
-
shouldAct: true,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function looksLikeDefinitionCreateIntent(text) {
|
|
168
|
-
return hasCadenceHint(text);
|
|
169
|
-
}
|
|
170
|
-
function looksLikeGoalCreateIntent(text) {
|
|
171
|
-
return textMatchesAnyTerm(text, LIFE_GOAL_TERMS) && !hasCadenceHint(text);
|
|
172
|
-
}
|
|
173
|
-
function hasCadenceHint(text) {
|
|
174
|
-
return textMatchesAnyTerm(text, LIFE_CADENCE_TERMS);
|
|
175
|
-
}
|
|
176
|
-
function shouldForceLifeCreateExecution(args) {
|
|
177
|
-
if (args.operation !== "create_definition") {
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
const blockingFields = args.missing.filter((field) => field !== "title" && field !== "schedule");
|
|
181
|
-
if (blockingFields.length > 0) {
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
if (typeof args.title === "string" && args.title.trim().length > 0) {
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
if (normalizeCadenceDetail(detailObject(args.details, "cadence"))) {
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
function looksLikeCompletionReport(text) {
|
|
193
|
-
// Exclude overview queries — these mention "done/finish" but ask what's remaining
|
|
194
|
-
if (textMatchesAnyTerm(text, LIFE_OVERVIEW_TERMS)) {
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
// Exclude create-intent with cadence — "create a habit until I complete it"
|
|
198
|
-
if (textMatchesAnyTerm(text, LIFE_CADENCE_TERMS)) {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
return textMatchesAnyTerm(text, LIFE_COMPLETE_TERMS);
|
|
202
|
-
}
|
|
203
|
-
// ── Helpers ───────────────────────────────────────────
|
|
204
|
-
function requestedOwnership(domain) {
|
|
205
|
-
if (domain === "agent_ops") {
|
|
206
|
-
return { domain: "agent_ops", subjectType: "agent" };
|
|
207
|
-
}
|
|
208
|
-
return { domain: "user_lifeops", subjectType: "owner" };
|
|
209
|
-
}
|
|
210
|
-
function normalizeIntentText(value) {
|
|
211
|
-
return normalizeLifeInputText(value).toLowerCase();
|
|
212
|
-
}
|
|
213
|
-
function normalizeLifeInputText(value) {
|
|
214
|
-
return value
|
|
215
|
-
.replace(/[\u00a0\u1680\u2000-\u200b\u202f\u205f\u3000]/g, " ")
|
|
216
|
-
.replace(/\s+/g, " ")
|
|
217
|
-
.trim();
|
|
218
|
-
}
|
|
219
|
-
function normalizeTitle(value) {
|
|
220
|
-
return normalizeIntentText(value);
|
|
221
|
-
}
|
|
222
|
-
function matchByTitle(entries, targetTitle) {
|
|
223
|
-
const normalized = normalizeTitle(targetTitle);
|
|
224
|
-
return (entries.find((e) => normalizeTitle(e.definition?.title ?? e.goal?.title ?? "") ===
|
|
225
|
-
normalized) ??
|
|
226
|
-
entries.find((e) => normalizeTitle(e.definition?.title ?? e.goal?.title ?? "").includes(normalized)) ??
|
|
227
|
-
null);
|
|
228
|
-
}
|
|
229
|
-
function coerceDeferredLifeDraft(value) {
|
|
230
|
-
if (!value || typeof value !== "object") {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
const record = value;
|
|
234
|
-
const operation = record.operation;
|
|
235
|
-
const intent = typeof record.intent === "string" ? record.intent.trim() : "";
|
|
236
|
-
const request = record.request && typeof record.request === "object"
|
|
237
|
-
? record.request
|
|
238
|
-
: null;
|
|
239
|
-
const createdAt = typeof record.createdAt === "number" && Number.isFinite(record.createdAt)
|
|
240
|
-
? record.createdAt
|
|
241
|
-
: undefined;
|
|
242
|
-
if (!request || !intent) {
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
const title = typeof request.title === "string" ? request.title.trim() : "";
|
|
246
|
-
if (!title) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
if (operation === "create_definition") {
|
|
250
|
-
const kind = typeof request.kind === "string"
|
|
251
|
-
? request.kind
|
|
252
|
-
: null;
|
|
253
|
-
const cadence = request.cadence;
|
|
254
|
-
if (!kind || !cadence) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
return {
|
|
258
|
-
createdAt,
|
|
259
|
-
intent,
|
|
260
|
-
operation,
|
|
261
|
-
request: {
|
|
262
|
-
cadence,
|
|
263
|
-
description: typeof request.description === "string"
|
|
264
|
-
? request.description
|
|
265
|
-
: undefined,
|
|
266
|
-
goalRef: typeof request.goalRef === "string" ? request.goalRef : undefined,
|
|
267
|
-
kind,
|
|
268
|
-
priority: typeof request.priority === "number" ? request.priority : undefined,
|
|
269
|
-
progressionRule: request.progressionRule,
|
|
270
|
-
reminderPlan: request.reminderPlan,
|
|
271
|
-
timezone: typeof request.timezone === "string" ? request.timezone : undefined,
|
|
272
|
-
title,
|
|
273
|
-
metadata: request.metadata && typeof request.metadata === "object"
|
|
274
|
-
? request.metadata
|
|
275
|
-
: undefined,
|
|
276
|
-
windowPolicy: request.windowPolicy,
|
|
277
|
-
websiteAccess: request.websiteAccess,
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
if (operation === "create_goal") {
|
|
282
|
-
return {
|
|
283
|
-
createdAt,
|
|
284
|
-
intent,
|
|
285
|
-
operation,
|
|
286
|
-
request: {
|
|
287
|
-
cadence: request.cadence,
|
|
288
|
-
description: typeof request.description === "string"
|
|
289
|
-
? request.description
|
|
290
|
-
: undefined,
|
|
291
|
-
metadata: request.metadata && typeof request.metadata === "object"
|
|
292
|
-
? request.metadata
|
|
293
|
-
: undefined,
|
|
294
|
-
successCriteria: request.successCriteria,
|
|
295
|
-
supportStrategy: request.supportStrategy,
|
|
296
|
-
title,
|
|
297
|
-
},
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
function stateActionResults(state) {
|
|
303
|
-
if (!state || typeof state !== "object") {
|
|
304
|
-
return [];
|
|
305
|
-
}
|
|
306
|
-
const stateRecord = state;
|
|
307
|
-
const data = stateRecord.data && typeof stateRecord.data === "object"
|
|
308
|
-
? stateRecord.data
|
|
309
|
-
: undefined;
|
|
310
|
-
const providerResults = data?.providers && typeof data.providers === "object"
|
|
311
|
-
? data.providers
|
|
312
|
-
: undefined;
|
|
313
|
-
const providerActionState = providerResults?.ACTION_STATE &&
|
|
314
|
-
typeof providerResults.ACTION_STATE === "object"
|
|
315
|
-
? providerResults.ACTION_STATE
|
|
316
|
-
: undefined;
|
|
317
|
-
const providerActionStateData = providerActionState?.data && typeof providerActionState.data === "object"
|
|
318
|
-
? providerActionState.data
|
|
319
|
-
: undefined;
|
|
320
|
-
const providerRecentMessages = providerResults?.RECENT_MESSAGES &&
|
|
321
|
-
typeof providerResults.RECENT_MESSAGES === "object"
|
|
322
|
-
? providerResults.RECENT_MESSAGES
|
|
323
|
-
: undefined;
|
|
324
|
-
const providerRecentMessagesData = providerRecentMessages?.data &&
|
|
325
|
-
typeof providerRecentMessages.data === "object"
|
|
326
|
-
? providerRecentMessages.data
|
|
327
|
-
: undefined;
|
|
328
|
-
const candidates = [
|
|
329
|
-
data?.actionResults,
|
|
330
|
-
providerActionStateData?.actionResults,
|
|
331
|
-
providerActionStateData?.recentActionMemories,
|
|
332
|
-
providerRecentMessagesData?.actionResults,
|
|
333
|
-
].filter(Array.isArray);
|
|
334
|
-
if (candidates.length === 0) {
|
|
335
|
-
return [];
|
|
336
|
-
}
|
|
337
|
-
return candidates.flatMap((entries) => entries.flatMap((entry) => {
|
|
338
|
-
if (!entry || typeof entry !== "object") {
|
|
339
|
-
return [];
|
|
340
|
-
}
|
|
341
|
-
if ("content" in entry) {
|
|
342
|
-
const content = entry.content &&
|
|
343
|
-
typeof entry.content === "object"
|
|
344
|
-
? entry
|
|
345
|
-
.content
|
|
346
|
-
: null;
|
|
347
|
-
if (!content) {
|
|
348
|
-
return [];
|
|
349
|
-
}
|
|
350
|
-
const contentData = content.data && typeof content.data === "object"
|
|
351
|
-
? { ...content.data }
|
|
352
|
-
: {};
|
|
353
|
-
if (typeof content.actionName === "string" &&
|
|
354
|
-
typeof contentData.actionName !== "string") {
|
|
355
|
-
contentData.actionName = content.actionName;
|
|
356
|
-
}
|
|
357
|
-
return [
|
|
358
|
-
{
|
|
359
|
-
success: content.actionStatus !== "failed",
|
|
360
|
-
text: typeof content.text === "string" ? content.text : undefined,
|
|
361
|
-
data: contentData,
|
|
362
|
-
error: typeof content.error === "string" ? content.error : undefined,
|
|
363
|
-
},
|
|
364
|
-
];
|
|
365
|
-
}
|
|
366
|
-
return [entry];
|
|
367
|
-
}));
|
|
368
|
-
}
|
|
369
|
-
function stateMessageDrafts(state) {
|
|
370
|
-
if (!state || typeof state !== "object") {
|
|
371
|
-
return [];
|
|
372
|
-
}
|
|
373
|
-
const stateRecord = state;
|
|
374
|
-
const data = stateRecord.data && typeof stateRecord.data === "object"
|
|
375
|
-
? stateRecord.data
|
|
376
|
-
: undefined;
|
|
377
|
-
const providerResults = data?.providers && typeof data.providers === "object"
|
|
378
|
-
? data.providers
|
|
379
|
-
: undefined;
|
|
380
|
-
const providerRecentMessages = providerResults?.RECENT_MESSAGES &&
|
|
381
|
-
typeof providerResults.RECENT_MESSAGES === "object"
|
|
382
|
-
? providerResults.RECENT_MESSAGES
|
|
383
|
-
: undefined;
|
|
384
|
-
const providerRecentMessagesData = providerRecentMessages?.data &&
|
|
385
|
-
typeof providerRecentMessages.data === "object"
|
|
386
|
-
? providerRecentMessages.data
|
|
387
|
-
: undefined;
|
|
388
|
-
const recentMessagesData = [
|
|
389
|
-
stateRecord.recentMessagesData,
|
|
390
|
-
stateRecord.recentMessages,
|
|
391
|
-
providerRecentMessagesData?.recentMessages,
|
|
392
|
-
].find(Array.isArray);
|
|
393
|
-
if (!Array.isArray(recentMessagesData)) {
|
|
394
|
-
return [];
|
|
395
|
-
}
|
|
396
|
-
const drafts = [];
|
|
397
|
-
for (const item of recentMessagesData) {
|
|
398
|
-
if (!item || typeof item !== "object") {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
const content = item.content;
|
|
402
|
-
if (!content || typeof content !== "object") {
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
const contentRecord = content;
|
|
406
|
-
const candidate = coerceDeferredLifeDraft(contentRecord.lifeDraft) ??
|
|
407
|
-
coerceDeferredLifeDraft(contentRecord.data && typeof contentRecord.data === "object"
|
|
408
|
-
? contentRecord.data.lifeDraft
|
|
409
|
-
: undefined);
|
|
410
|
-
if (candidate) {
|
|
411
|
-
drafts.push(candidate);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return drafts;
|
|
415
|
-
}
|
|
416
|
-
function stateRecentMessageEntries(state) {
|
|
417
|
-
if (!state || typeof state !== "object") {
|
|
418
|
-
return [];
|
|
419
|
-
}
|
|
420
|
-
const stateRecord = state;
|
|
421
|
-
const data = stateRecord.data && typeof stateRecord.data === "object"
|
|
422
|
-
? stateRecord.data
|
|
423
|
-
: undefined;
|
|
424
|
-
const providerResults = data?.providers && typeof data.providers === "object"
|
|
425
|
-
? data.providers
|
|
426
|
-
: undefined;
|
|
427
|
-
const providerRecentMessages = providerResults?.RECENT_MESSAGES &&
|
|
428
|
-
typeof providerResults.RECENT_MESSAGES === "object"
|
|
429
|
-
? providerResults.RECENT_MESSAGES
|
|
430
|
-
: undefined;
|
|
431
|
-
const providerRecentMessagesData = providerRecentMessages?.data &&
|
|
432
|
-
typeof providerRecentMessages.data === "object"
|
|
433
|
-
? providerRecentMessages.data
|
|
434
|
-
: undefined;
|
|
435
|
-
const recentMessagesData = [
|
|
436
|
-
stateRecord.recentMessagesData,
|
|
437
|
-
stateRecord.recentMessages,
|
|
438
|
-
providerRecentMessagesData?.recentMessages,
|
|
439
|
-
].find(Array.isArray);
|
|
440
|
-
if (!Array.isArray(recentMessagesData)) {
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
|
-
return recentMessagesData.filter((item) => Boolean(item) && typeof item === "object");
|
|
444
|
-
}
|
|
445
|
-
function isDeferredLifeDraftMessageEntry(item) {
|
|
446
|
-
const content = item.content && typeof item.content === "object"
|
|
447
|
-
? item.content
|
|
448
|
-
: null;
|
|
449
|
-
if (!content) {
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
return Boolean(coerceDeferredLifeDraft(content.lifeDraft) ??
|
|
453
|
-
coerceDeferredLifeDraft(content.data && typeof content.data === "object"
|
|
454
|
-
? content.data.lifeDraft
|
|
455
|
-
: undefined));
|
|
456
|
-
}
|
|
457
|
-
function countTurnsSinceLatestDeferredLifeDraft(state) {
|
|
458
|
-
const entries = stateRecentMessageEntries(state);
|
|
459
|
-
if (entries.length === 0) {
|
|
460
|
-
return undefined;
|
|
461
|
-
}
|
|
462
|
-
let latestDraftIndex = -1;
|
|
463
|
-
for (let index = entries.length - 1; index >= 0; index--) {
|
|
464
|
-
if (isDeferredLifeDraftMessageEntry(entries[index])) {
|
|
465
|
-
latestDraftIndex = index;
|
|
466
|
-
break;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
if (latestDraftIndex < 0) {
|
|
470
|
-
return undefined;
|
|
471
|
-
}
|
|
472
|
-
let turns = 0;
|
|
473
|
-
for (const entry of entries.slice(latestDraftIndex + 1)) {
|
|
474
|
-
const content = entry.content && typeof entry.content === "object"
|
|
475
|
-
? entry.content
|
|
476
|
-
: null;
|
|
477
|
-
if (!content || isDeferredLifeDraftMessageEntry(entry)) {
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
if (typeof content.text === "string" && content.text.trim().length > 0) {
|
|
481
|
-
turns++;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
return turns;
|
|
485
|
-
}
|
|
486
|
-
function latestDeferredLifeDraft(state) {
|
|
487
|
-
for (const result of [...stateActionResults(state)].reverse()) {
|
|
488
|
-
const resultData = result.data && typeof result.data === "object"
|
|
489
|
-
? result.data
|
|
490
|
-
: null;
|
|
491
|
-
const completedCreate = result.success &&
|
|
492
|
-
resultData &&
|
|
493
|
-
!coerceDeferredLifeDraft(resultData.lifeDraft) &&
|
|
494
|
-
((resultData.definition && typeof resultData.definition === "object") ||
|
|
495
|
-
(resultData.goal && typeof resultData.goal === "object"));
|
|
496
|
-
if (completedCreate) {
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
const candidate = coerceDeferredLifeDraft(result.data?.lifeDraft);
|
|
500
|
-
if (candidate) {
|
|
501
|
-
return candidate;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
const messageDrafts = stateMessageDrafts(state);
|
|
505
|
-
return messageDrafts.length > 0
|
|
506
|
-
? messageDrafts[messageDrafts.length - 1]
|
|
507
|
-
: null;
|
|
508
|
-
}
|
|
509
|
-
function looksLikeDeferredLifeConfirmation(text) {
|
|
510
|
-
const normalized = text.trim().toLowerCase();
|
|
511
|
-
if (!normalized)
|
|
512
|
-
return false;
|
|
513
|
-
if (textMatchesAnyTerm(normalized, LIFE_NEGATIVE_TERMS))
|
|
514
|
-
return false;
|
|
515
|
-
if (textMatchesAnyTerm(normalized, LIFE_DRAFT_EDIT_TERMS))
|
|
516
|
-
return false;
|
|
517
|
-
return textMatchesAnyTerm(normalized, LIFE_AFFIRMATIVE_TERMS);
|
|
518
|
-
}
|
|
519
|
-
function normalizeDeferredLifeDraftReference(value) {
|
|
520
|
-
return normalizeIntentText(value ?? "")
|
|
521
|
-
.replace(/\b(the|that|this|a|an|my|routine|habit|task|goal|reminder)\b/g, " ")
|
|
522
|
-
.replace(/\s+/g, " ")
|
|
523
|
-
.trim();
|
|
524
|
-
}
|
|
525
|
-
function matchesDeferredLifeDraftTitle(value, draft) {
|
|
526
|
-
if (!draft) {
|
|
527
|
-
return false;
|
|
528
|
-
}
|
|
529
|
-
const normalizedValue = normalizeDeferredLifeDraftReference(value);
|
|
530
|
-
const normalizedDraftTitle = normalizeDeferredLifeDraftReference(draft.request.title);
|
|
531
|
-
if (!normalizedValue || !normalizedDraftTitle) {
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
return (normalizedValue === normalizedDraftTitle ||
|
|
535
|
-
normalizedValue.includes(normalizedDraftTitle) ||
|
|
536
|
-
normalizedDraftTitle.includes(normalizedValue));
|
|
537
|
-
}
|
|
538
|
-
function deferredLifeDraftExpiryReason(args) {
|
|
539
|
-
if (!args.draft) {
|
|
540
|
-
return null;
|
|
541
|
-
}
|
|
542
|
-
if (args.draft.createdAt) {
|
|
543
|
-
const ageMs = Date.now() - args.draft.createdAt;
|
|
544
|
-
if (ageMs >= DRAFT_EXPIRY_MS) {
|
|
545
|
-
return "age";
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
if (typeof args.turnsSinceDraft === "number" &&
|
|
549
|
-
args.turnsSinceDraft >= DRAFT_MAX_TURNS) {
|
|
550
|
-
return "turns";
|
|
551
|
-
}
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
function looksLikeDeferredLifeDraftEdit(text) {
|
|
555
|
-
const normalized = normalizeIntentText(text);
|
|
556
|
-
if (!normalized || looksLikeDeferredLifeConfirmation(text))
|
|
557
|
-
return false;
|
|
558
|
-
if (textMatchesAnyTerm(normalized, LIFE_NEGATIVE_TERMS))
|
|
559
|
-
return false;
|
|
560
|
-
if (textMatchesAnyTerm(normalized, LIFE_DRAFT_EDIT_TERMS))
|
|
561
|
-
return true;
|
|
562
|
-
return hasCadenceHint(normalized) || /\b\d+\b/.test(normalized);
|
|
563
|
-
}
|
|
564
|
-
async function extractDeferredLifeDraftFollowupWithLlm(args) {
|
|
565
|
-
if (typeof args.runtime.useModel !== "function") {
|
|
566
|
-
return null;
|
|
567
|
-
}
|
|
568
|
-
const recentConversation = await recentConversationTexts({
|
|
569
|
-
runtime: args.runtime,
|
|
570
|
-
message: args.message,
|
|
571
|
-
state: args.state,
|
|
572
|
-
limit: 12,
|
|
573
|
-
});
|
|
574
|
-
const prompt = [
|
|
575
|
-
"Decide how the assistant should interpret the user's follow-up to a previewed LifeOps draft that has not been saved yet.",
|
|
576
|
-
"Use the current message, the draft summary, and recent conversation.",
|
|
577
|
-
"The user may speak in any language.",
|
|
578
|
-
"",
|
|
579
|
-
"Return ONLY valid JSON with exactly this shape:",
|
|
580
|
-
'{"mode":"confirm"|"edit"|"cancel"|"none"}',
|
|
581
|
-
"",
|
|
582
|
-
"Choose confirm when the user clearly approves saving the current draft now.",
|
|
583
|
-
"Choose edit when the user wants to change the draft or continue specifying it before saving.",
|
|
584
|
-
"Choose cancel when the user says not to save it, never mind, not yet, hold off, or equivalent.",
|
|
585
|
-
"Choose none when the follow-up is unrelated or too ambiguous to attach to the draft.",
|
|
586
|
-
"",
|
|
587
|
-
"Previewed draft:",
|
|
588
|
-
stringifyDeferredLifeDraftForPrompt(args.draft),
|
|
589
|
-
"",
|
|
590
|
-
`Current user message: ${JSON.stringify(args.currentText)}`,
|
|
591
|
-
`Recent conversation: ${JSON.stringify(recentConversation.join("\n"))}`,
|
|
592
|
-
].join("\n");
|
|
593
|
-
try {
|
|
594
|
-
const result = await args.runtime.useModel(ModelType.TEXT_LARGE, {
|
|
595
|
-
prompt,
|
|
596
|
-
});
|
|
597
|
-
const raw = typeof result === "string" ? result : "";
|
|
598
|
-
const parsed = parseJSONObjectFromText(raw);
|
|
599
|
-
const mode = parsed && typeof parsed.mode === "string"
|
|
600
|
-
? parsed.mode.trim().toLowerCase()
|
|
601
|
-
: "";
|
|
602
|
-
switch (mode) {
|
|
603
|
-
case "confirm":
|
|
604
|
-
case "edit":
|
|
605
|
-
case "cancel":
|
|
606
|
-
return mode;
|
|
607
|
-
default:
|
|
608
|
-
return null;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
catch {
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
function stringifyDeferredLifeDraftForPrompt(draft) {
|
|
616
|
-
if (draft.operation === "create_definition") {
|
|
617
|
-
return JSON.stringify({
|
|
618
|
-
operation: draft.operation,
|
|
619
|
-
title: draft.request.title,
|
|
620
|
-
kind: draft.request.kind,
|
|
621
|
-
cadence: draft.request.cadence,
|
|
622
|
-
timezone: draft.request.timezone ?? null,
|
|
623
|
-
description: draft.request.description ?? null,
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
return JSON.stringify({
|
|
627
|
-
operation: draft.operation,
|
|
628
|
-
title: draft.request.title,
|
|
629
|
-
cadence: draft.request.cadence ?? null,
|
|
630
|
-
description: draft.request.description ?? null,
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
function resolveDeferredLifeDraftReuseMode(args) {
|
|
634
|
-
if (!args.draft) {
|
|
635
|
-
return null;
|
|
636
|
-
}
|
|
637
|
-
if (deferredLifeDraftExpiryReason(args)) {
|
|
638
|
-
return null;
|
|
639
|
-
}
|
|
640
|
-
if (detailBoolean(args.details, "confirmed") === true) {
|
|
641
|
-
return "confirm";
|
|
642
|
-
}
|
|
643
|
-
const words = args.currentText.trim().split(/\s+/).filter(Boolean);
|
|
644
|
-
const isConfirmationFollowup = words.length > 0 &&
|
|
645
|
-
words.length <= 6 &&
|
|
646
|
-
!hasCadenceHint(args.currentText.toLowerCase()) &&
|
|
647
|
-
looksLikeDeferredLifeConfirmation(args.currentText);
|
|
648
|
-
if (isConfirmationFollowup) {
|
|
649
|
-
if (args.explicitAction &&
|
|
650
|
-
ACTION_TO_OPERATION[args.explicitAction] !== args.draft.operation) {
|
|
651
|
-
return null;
|
|
652
|
-
}
|
|
653
|
-
return "confirm";
|
|
654
|
-
}
|
|
655
|
-
const explicitReferenceMatchesDraft = matchesDeferredLifeDraftTitle(args.title, args.draft) ||
|
|
656
|
-
matchesDeferredLifeDraftTitle(args.target, args.draft);
|
|
657
|
-
if (looksLikeDeferredLifeConfirmation(args.currentText)) {
|
|
658
|
-
if (args.explicitAction &&
|
|
659
|
-
ACTION_TO_OPERATION[args.explicitAction] !== args.draft.operation) {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
if (explicitReferenceMatchesDraft) {
|
|
663
|
-
return "confirm";
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
const normalizedCurrentText = normalizeIntentText(args.currentText);
|
|
667
|
-
const normalizedParamsIntent = typeof args.paramsIntent === "string" && args.paramsIntent.trim().length > 0
|
|
668
|
-
? normalizeIntentText(args.paramsIntent)
|
|
669
|
-
: "";
|
|
670
|
-
if (normalizedParamsIntent &&
|
|
671
|
-
normalizedParamsIntent !== normalizedCurrentText &&
|
|
672
|
-
!looksLikeDeferredLifeConfirmation(args.paramsIntent ?? "")) {
|
|
673
|
-
return null;
|
|
674
|
-
}
|
|
675
|
-
if (args.explicitAction &&
|
|
676
|
-
ACTION_TO_OPERATION[args.explicitAction] !== args.draft.operation) {
|
|
677
|
-
return null;
|
|
678
|
-
}
|
|
679
|
-
if (args.llmMode === "confirm" || args.llmMode === "edit") {
|
|
680
|
-
return args.llmMode;
|
|
681
|
-
}
|
|
682
|
-
if (args.title || args.target) {
|
|
683
|
-
return null;
|
|
684
|
-
}
|
|
685
|
-
return looksLikeDeferredLifeDraftEdit(args.currentText) ? "edit" : null;
|
|
686
|
-
}
|
|
687
|
-
async function resolveGoal(service, target, domain) {
|
|
688
|
-
if (!target)
|
|
689
|
-
return null;
|
|
690
|
-
const goals = (await service.listGoals()).filter((e) => domain ? e.goal.domain === domain : true);
|
|
691
|
-
return goals.find((e) => e.goal.id === target) ?? matchByTitle(goals, target);
|
|
692
|
-
}
|
|
693
|
-
async function resolveDefinition(service, target, domain) {
|
|
694
|
-
if (!target)
|
|
695
|
-
return null;
|
|
696
|
-
const defs = (await service.listDefinitions()).filter((e) => domain ? e.definition.domain === domain : true);
|
|
697
|
-
return (defs.find((e) => e.definition.id === target) ?? matchByTitle(defs, target));
|
|
698
|
-
}
|
|
699
|
-
function tokenizeTitle(value) {
|
|
700
|
-
return normalizeTitle(value)
|
|
701
|
-
.split(" ")
|
|
702
|
-
.filter((token) => token.length >= 3);
|
|
703
|
-
}
|
|
704
|
-
async function resolveDefinitionFromIntent(service, target, intent, domain) {
|
|
705
|
-
const direct = await resolveDefinition(service, target, domain);
|
|
706
|
-
if (direct) {
|
|
707
|
-
return direct;
|
|
708
|
-
}
|
|
709
|
-
const defs = (await service.listDefinitions()).filter((entry) => domain ? entry.definition.domain === domain : true);
|
|
710
|
-
const intentTokens = new Set(tokenizeTitle(intent));
|
|
711
|
-
let best = null;
|
|
712
|
-
let bestScore = 0;
|
|
713
|
-
let tied = false;
|
|
714
|
-
for (const entry of defs) {
|
|
715
|
-
const title = normalizeTitle(entry.definition.title);
|
|
716
|
-
if (title.length > 0 && normalizeTitle(intent).includes(title)) {
|
|
717
|
-
return entry;
|
|
718
|
-
}
|
|
719
|
-
const overlap = tokenizeTitle(entry.definition.title).filter((token) => intentTokens.has(token)).length;
|
|
720
|
-
if (overlap === 0) {
|
|
721
|
-
continue;
|
|
722
|
-
}
|
|
723
|
-
if (overlap > bestScore) {
|
|
724
|
-
best = entry;
|
|
725
|
-
bestScore = overlap;
|
|
726
|
-
tied = false;
|
|
727
|
-
continue;
|
|
728
|
-
}
|
|
729
|
-
if (overlap === bestScore) {
|
|
730
|
-
tied = true;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return bestScore > 0 && !tied ? best : null;
|
|
734
|
-
}
|
|
735
|
-
function formatOccurrenceDisambiguationLabel(occurrence) {
|
|
736
|
-
const hints = [];
|
|
737
|
-
if (typeof occurrence.windowName === "string" &&
|
|
738
|
-
occurrence.windowName.trim()) {
|
|
739
|
-
hints.push(occurrence.windowName.trim());
|
|
740
|
-
}
|
|
741
|
-
if (occurrence.dueAt) {
|
|
742
|
-
const dueAt = new Date(occurrence.dueAt);
|
|
743
|
-
if (!Number.isNaN(dueAt.getTime())) {
|
|
744
|
-
hints.push(dueAt.toLocaleString(undefined, {
|
|
745
|
-
month: "short",
|
|
746
|
-
day: "numeric",
|
|
747
|
-
hour: "numeric",
|
|
748
|
-
minute: "2-digit",
|
|
749
|
-
}));
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return hints.length > 0
|
|
753
|
-
? `${occurrence.title} (${hints.join(", ")})`
|
|
754
|
-
: occurrence.title;
|
|
755
|
-
}
|
|
756
|
-
async function resolveOccurrence(service, target, domain) {
|
|
757
|
-
if (!target)
|
|
758
|
-
return { match: null, ambiguousCandidates: [] };
|
|
759
|
-
const overview = await service.getOverview();
|
|
760
|
-
const all = [
|
|
761
|
-
...overview.owner.occurrences,
|
|
762
|
-
...overview.agentOps.occurrences,
|
|
763
|
-
].filter((o) => (domain ? o.domain === domain : true));
|
|
764
|
-
const normalized = normalizeTitle(target);
|
|
765
|
-
// Exact ID match
|
|
766
|
-
const byId = all.find((o) => o.id === target);
|
|
767
|
-
if (byId)
|
|
768
|
-
return { match: byId, ambiguousCandidates: [] };
|
|
769
|
-
// Exact normalized-title match
|
|
770
|
-
const exactMatches = all.filter((o) => normalizeTitle(o.title) === normalized);
|
|
771
|
-
if (exactMatches.length === 1) {
|
|
772
|
-
return { match: exactMatches[0], ambiguousCandidates: [] };
|
|
773
|
-
}
|
|
774
|
-
if (exactMatches.length > 1) {
|
|
775
|
-
return {
|
|
776
|
-
match: null,
|
|
777
|
-
ambiguousCandidates: exactMatches.map(formatOccurrenceDisambiguationLabel),
|
|
778
|
-
};
|
|
779
|
-
}
|
|
780
|
-
// Substring matches — disambiguate when multiple
|
|
781
|
-
const substringMatches = all.filter((o) => normalizeTitle(o.title).includes(normalized));
|
|
782
|
-
if (substringMatches.length === 1) {
|
|
783
|
-
return { match: substringMatches[0], ambiguousCandidates: [] };
|
|
784
|
-
}
|
|
785
|
-
if (substringMatches.length > 1) {
|
|
786
|
-
// Prefer startsWith over generic includes
|
|
787
|
-
const startsWithMatches = substringMatches.filter((o) => normalizeTitle(o.title).startsWith(normalized));
|
|
788
|
-
if (startsWithMatches.length === 1) {
|
|
789
|
-
return { match: startsWithMatches[0], ambiguousCandidates: [] };
|
|
790
|
-
}
|
|
791
|
-
if (startsWithMatches.length > 1) {
|
|
792
|
-
return {
|
|
793
|
-
match: null,
|
|
794
|
-
ambiguousCandidates: startsWithMatches.map(formatOccurrenceDisambiguationLabel),
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
// Still ambiguous — return candidates for the caller to list
|
|
798
|
-
return {
|
|
799
|
-
match: null,
|
|
800
|
-
ambiguousCandidates: substringMatches.map(formatOccurrenceDisambiguationLabel),
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
const targetTokens = normalized.split(/\s+/).filter(Boolean);
|
|
804
|
-
if (targetTokens.length > 1) {
|
|
805
|
-
const tokenSetMatches = all.filter((occurrence) => {
|
|
806
|
-
const occurrenceTokens = new Set(normalizeTitle(occurrence.title).split(/\s+/).filter(Boolean));
|
|
807
|
-
return targetTokens.every((token) => occurrenceTokens.has(token));
|
|
808
|
-
});
|
|
809
|
-
if (tokenSetMatches.length === 1) {
|
|
810
|
-
return { match: tokenSetMatches[0], ambiguousCandidates: [] };
|
|
811
|
-
}
|
|
812
|
-
if (tokenSetMatches.length > 1) {
|
|
813
|
-
return {
|
|
814
|
-
match: null,
|
|
815
|
-
ambiguousCandidates: tokenSetMatches.map(formatOccurrenceDisambiguationLabel),
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
return { match: null, ambiguousCandidates: [] };
|
|
820
|
-
}
|
|
821
|
-
function deriveOccurrenceTargetFromIntent(intent, operation) {
|
|
822
|
-
const normalized = normalizeLifeInputText(intent);
|
|
823
|
-
if (!normalized) {
|
|
824
|
-
return null;
|
|
825
|
-
}
|
|
826
|
-
let candidate = normalized;
|
|
827
|
-
if (operation === "snooze_occurrence") {
|
|
828
|
-
candidate = candidate
|
|
829
|
-
.replace(/^(?:please\s+)?(?:snooze|postpone|push\b.*\bback|remind me later about)\s+/i, "")
|
|
830
|
-
.replace(/\bfor\s+\d+\s*(?:minutes?|hours?)\b.*$/i, "")
|
|
831
|
-
.replace(/\b(?:until|til)\b.+$/i, "")
|
|
832
|
-
.trim();
|
|
833
|
-
}
|
|
834
|
-
else if (operation === "skip_occurrence") {
|
|
835
|
-
candidate = candidate
|
|
836
|
-
.replace(/^(?:please\s+)?(?:skip|pass on)\s+/i, "")
|
|
837
|
-
.replace(/\b(?:today|tonight|for now)\b.*$/i, "")
|
|
838
|
-
.trim();
|
|
839
|
-
}
|
|
840
|
-
else if (operation === "complete_occurrence") {
|
|
841
|
-
candidate = candidate
|
|
842
|
-
.replace(/^(?:please\s+)?(?:mark\s+|i(?:'ve| have)?\s+|just\s+)?(?:done|completed|finished|did)\s+/i, "")
|
|
843
|
-
.replace(/\b(?:done|complete|completed|finished)\b.*$/i, "")
|
|
844
|
-
.trim();
|
|
845
|
-
}
|
|
846
|
-
return candidate.length > 0 ? candidate : null;
|
|
847
|
-
}
|
|
848
|
-
async function resolveOccurrenceWithIntentFallback(args) {
|
|
849
|
-
const direct = await resolveOccurrence(args.service, args.target, args.domain);
|
|
850
|
-
if (direct.match || direct.ambiguousCandidates.length > 0) {
|
|
851
|
-
return direct;
|
|
852
|
-
}
|
|
853
|
-
const fallbackTarget = deriveOccurrenceTargetFromIntent(args.intent, args.operation);
|
|
854
|
-
if (!fallbackTarget ||
|
|
855
|
-
(args.target &&
|
|
856
|
-
normalizeTitle(fallbackTarget) === normalizeTitle(args.target))) {
|
|
857
|
-
return direct;
|
|
858
|
-
}
|
|
859
|
-
return resolveOccurrence(args.service, fallbackTarget, args.domain);
|
|
860
|
-
}
|
|
861
|
-
function summarizeCadence(cadence) {
|
|
862
|
-
switch (cadence.kind) {
|
|
863
|
-
case "once": {
|
|
864
|
-
const dueAt = new Date(cadence.dueAt);
|
|
865
|
-
if (Number.isNaN(dueAt.getTime())) {
|
|
866
|
-
return "once";
|
|
867
|
-
}
|
|
868
|
-
return `once on ${dueAt.toLocaleString(undefined, {
|
|
869
|
-
month: "short",
|
|
870
|
-
day: "numeric",
|
|
871
|
-
hour: "numeric",
|
|
872
|
-
minute: "2-digit",
|
|
873
|
-
timeZone: resolveDefaultTimeZone(),
|
|
874
|
-
})}`;
|
|
875
|
-
}
|
|
876
|
-
case "daily":
|
|
877
|
-
return `every day in ${cadence.windows.join(", ")}`;
|
|
878
|
-
case "times_per_day":
|
|
879
|
-
return cadence.slots
|
|
880
|
-
.map((slot) => slot.label?.trim() || `${slot.minuteOfDay}`)
|
|
881
|
-
.filter(Boolean)
|
|
882
|
-
.join(" and ");
|
|
883
|
-
case "interval":
|
|
884
|
-
return `every ${cadence.everyMinutes} minutes in ${cadence.windows.join(", ")}`;
|
|
885
|
-
case "weekly":
|
|
886
|
-
return `weekly on ${cadence.weekdays
|
|
887
|
-
.map((weekday) => [
|
|
888
|
-
"Sunday",
|
|
889
|
-
"Monday",
|
|
890
|
-
"Tuesday",
|
|
891
|
-
"Wednesday",
|
|
892
|
-
"Thursday",
|
|
893
|
-
"Friday",
|
|
894
|
-
"Saturday",
|
|
895
|
-
][weekday] ?? String(weekday))
|
|
896
|
-
.join(", ")}`;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
function extractNaturalTimePhrase(intent) {
|
|
900
|
-
const normalized = normalizeLifeInputText(intent).toLowerCase();
|
|
901
|
-
if (/\bmornings?\s+only\b|\bmornings?\b/.test(normalized)) {
|
|
902
|
-
return "mornings now";
|
|
903
|
-
}
|
|
904
|
-
if (/\bafternoons?\s+only\b|\bafternoons?\b/.test(normalized)) {
|
|
905
|
-
return "afternoons now";
|
|
906
|
-
}
|
|
907
|
-
if (/\bevenings?\s+only\b|\bevenings?\b/.test(normalized)) {
|
|
908
|
-
return "evenings now";
|
|
909
|
-
}
|
|
910
|
-
if (/\bnights?\s+only\b|\bnights?\b/.test(normalized)) {
|
|
911
|
-
return "nights now";
|
|
912
|
-
}
|
|
913
|
-
const timeMatch = normalized.match(/\b(\d{1,2}(?::\d{2})?\s*(?:am|pm))\b/);
|
|
914
|
-
if (timeMatch?.[1]) {
|
|
915
|
-
return `${timeMatch[1].replace(/\s+/g, "")} now`;
|
|
916
|
-
}
|
|
917
|
-
return null;
|
|
918
|
-
}
|
|
919
|
-
function buildRuleBasedLifeReply(args) {
|
|
920
|
-
const context = args.context ?? {};
|
|
921
|
-
const updated = context.updated && typeof context.updated === "object"
|
|
922
|
-
? context.updated
|
|
923
|
-
: null;
|
|
924
|
-
const created = context.created && typeof context.created === "object"
|
|
925
|
-
? context.created
|
|
926
|
-
: null;
|
|
927
|
-
const title = (typeof updated?.title === "string" ? updated.title : null) ??
|
|
928
|
-
(typeof created?.title === "string" ? created.title : null) ??
|
|
929
|
-
(typeof context.title === "string" ? context.title : null) ??
|
|
930
|
-
null;
|
|
931
|
-
const timePhrase = extractNaturalTimePhrase(args.intent);
|
|
932
|
-
switch (args.scenario) {
|
|
933
|
-
case "updated_definition":
|
|
934
|
-
if (title && timePhrase) {
|
|
935
|
-
return `${title} is set for ${timePhrase}.`;
|
|
936
|
-
}
|
|
937
|
-
if (title) {
|
|
938
|
-
return `${title} is updated.`;
|
|
939
|
-
}
|
|
940
|
-
break;
|
|
941
|
-
case "deleted_definition":
|
|
942
|
-
if (title) {
|
|
943
|
-
return `${title} is off your list.`;
|
|
944
|
-
}
|
|
945
|
-
break;
|
|
946
|
-
case "deleted_goal":
|
|
947
|
-
if (title) {
|
|
948
|
-
return `${title} is off your goals list.`;
|
|
949
|
-
}
|
|
950
|
-
break;
|
|
951
|
-
case "completed_occurrence":
|
|
952
|
-
if (title) {
|
|
953
|
-
return `Marked ${title} done.`;
|
|
954
|
-
}
|
|
955
|
-
break;
|
|
956
|
-
case "skipped_occurrence":
|
|
957
|
-
if (title) {
|
|
958
|
-
return `Okay, skipping ${title} for now.`;
|
|
959
|
-
}
|
|
960
|
-
break;
|
|
961
|
-
case "snoozed_occurrence":
|
|
962
|
-
if (title) {
|
|
963
|
-
return `Okay, I'll bring ${title} back a bit later.`;
|
|
964
|
-
}
|
|
965
|
-
break;
|
|
966
|
-
default:
|
|
967
|
-
break;
|
|
968
|
-
}
|
|
969
|
-
return args.fallback;
|
|
970
|
-
}
|
|
971
|
-
async function renderLifeActionReply(args) {
|
|
972
|
-
const { runtime, message, state, intent, scenario, fallback, context } = args;
|
|
973
|
-
const naturalFallback = buildRuleBasedLifeReply({
|
|
974
|
-
scenario,
|
|
975
|
-
intent,
|
|
976
|
-
fallback,
|
|
977
|
-
context,
|
|
978
|
-
});
|
|
979
|
-
return renderGroundedActionReply({
|
|
980
|
-
runtime,
|
|
981
|
-
message,
|
|
982
|
-
state,
|
|
983
|
-
intent,
|
|
984
|
-
domain: "lifeops",
|
|
985
|
-
scenario,
|
|
986
|
-
fallback: naturalFallback,
|
|
987
|
-
context,
|
|
988
|
-
preferCharacterVoice: true,
|
|
989
|
-
additionalRules: [
|
|
990
|
-
"Mirror the user's phrasing for time and date when possible.",
|
|
991
|
-
"Prefer phrases like tomorrow morning, every night, 7 am, or the user's own wording over robotic schedule language.",
|
|
992
|
-
"Never surface raw ISO timestamps unless the user used raw ISO timestamps.",
|
|
993
|
-
"If this is a preview, make clear it is not saved yet and the user can confirm or change it naturally.",
|
|
994
|
-
"If this is reply-only, do not pretend you saved or changed anything.",
|
|
995
|
-
],
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
function buildLifeClarificationFallback(args) {
|
|
999
|
-
const missing = new Set(args.missing);
|
|
1000
|
-
if (args.operation === "create_goal") {
|
|
1001
|
-
return "What do you want the goal to be?";
|
|
1002
|
-
}
|
|
1003
|
-
if (missing.has("title") && missing.has("schedule")) {
|
|
1004
|
-
return "What do you want the todo to be, and when should it happen?";
|
|
1005
|
-
}
|
|
1006
|
-
if (missing.has("title")) {
|
|
1007
|
-
return "What do you want it to be?";
|
|
1008
|
-
}
|
|
1009
|
-
if (missing.has("schedule")) {
|
|
1010
|
-
return "When should it happen?";
|
|
1011
|
-
}
|
|
1012
|
-
return "Tell me a bit more about what you want to set up.";
|
|
1013
|
-
}
|
|
1014
|
-
function buildLifeServiceErrorFallback(error, intent) {
|
|
1015
|
-
const normalized = error.message.toLowerCase();
|
|
1016
|
-
if (normalized.includes("utc 'z' suffix") ||
|
|
1017
|
-
normalized.includes("local datetime without 'z'") ||
|
|
1018
|
-
normalized.includes("time didn't parse") ||
|
|
1019
|
-
normalized.includes("invalid dueat") ||
|
|
1020
|
-
normalized.includes("cadence.dueat")) {
|
|
1021
|
-
return `I couldn't pin down the reminder time from "${intent}". Tell me the time again in plain language, like "Friday at 8 pm Pacific."`;
|
|
1022
|
-
}
|
|
1023
|
-
if (normalized.includes("when windowpreset is not provided") ||
|
|
1024
|
-
normalized.includes("startat is required")) {
|
|
1025
|
-
return "I still need the time for that reminder. Tell me when it should happen.";
|
|
1026
|
-
}
|
|
1027
|
-
if (error.status === 429 || normalized.includes("rate limit")) {
|
|
1028
|
-
return "LifeOps is rate-limited right now. Try again in a bit.";
|
|
1029
|
-
}
|
|
1030
|
-
return "I couldn't finish that LifeOps change yet. Tell me the task and timing again, and I'll try it a different way.";
|
|
1031
|
-
}
|
|
1032
|
-
// ── Calendar/email formatters ─────────────────────────
|
|
1033
|
-
const DEFAULT_WINDOW_SLOT_TIMES = {
|
|
1034
|
-
morning: {
|
|
1035
|
-
minuteOfDay: 8 * 60,
|
|
1036
|
-
durationMinutes: 45,
|
|
1037
|
-
label: "Morning",
|
|
1038
|
-
},
|
|
1039
|
-
afternoon: {
|
|
1040
|
-
minuteOfDay: 13 * 60,
|
|
1041
|
-
durationMinutes: 45,
|
|
1042
|
-
label: "Afternoon",
|
|
1043
|
-
},
|
|
1044
|
-
evening: {
|
|
1045
|
-
minuteOfDay: 18 * 60,
|
|
1046
|
-
durationMinutes: 45,
|
|
1047
|
-
label: "Evening",
|
|
1048
|
-
},
|
|
1049
|
-
night: {
|
|
1050
|
-
minuteOfDay: 21 * 60,
|
|
1051
|
-
durationMinutes: 45,
|
|
1052
|
-
label: "Night",
|
|
1053
|
-
},
|
|
1054
|
-
};
|
|
1055
|
-
function buildSlotsFromWindows(windows) {
|
|
1056
|
-
return windows.map((window, index) => {
|
|
1057
|
-
const preset = DEFAULT_WINDOW_SLOT_TIMES[window];
|
|
1058
|
-
return {
|
|
1059
|
-
key: windows.indexOf(window) === index ? window : `${window}-${index + 1}`,
|
|
1060
|
-
label: preset.label,
|
|
1061
|
-
minuteOfDay: preset.minuteOfDay,
|
|
1062
|
-
durationMinutes: preset.durationMinutes,
|
|
1063
|
-
};
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
function buildDistributedDailySlots(count) {
|
|
1067
|
-
const normalizedCount = Math.max(1, Math.min(6, count));
|
|
1068
|
-
const presets = {
|
|
1069
|
-
1: [9 * 60],
|
|
1070
|
-
2: [8 * 60, 21 * 60],
|
|
1071
|
-
3: [8 * 60, 13 * 60, 20 * 60],
|
|
1072
|
-
4: [8 * 60, 12 * 60, 16 * 60, 20 * 60],
|
|
1073
|
-
5: [8 * 60, 11 * 60, 14 * 60, 17 * 60, 20 * 60],
|
|
1074
|
-
6: [8 * 60, 10 * 60, 12 * 60, 14 * 60, 17 * 60, 20 * 60],
|
|
1075
|
-
};
|
|
1076
|
-
const minutes = presets[normalizedCount] ?? presets[1];
|
|
1077
|
-
return minutes.map((minuteOfDay, index) => ({
|
|
1078
|
-
key: `slot-${index + 1}`,
|
|
1079
|
-
label: `Time ${index + 1}`,
|
|
1080
|
-
minuteOfDay,
|
|
1081
|
-
durationMinutes: 45,
|
|
1082
|
-
}));
|
|
1083
|
-
}
|
|
1084
|
-
function inferWindowFromMinuteOfDay(minuteOfDay) {
|
|
1085
|
-
if (minuteOfDay < 12 * 60) {
|
|
1086
|
-
return "morning";
|
|
1087
|
-
}
|
|
1088
|
-
if (minuteOfDay < 17 * 60) {
|
|
1089
|
-
return "afternoon";
|
|
1090
|
-
}
|
|
1091
|
-
if (minuteOfDay < 21 * 60) {
|
|
1092
|
-
return "evening";
|
|
1093
|
-
}
|
|
1094
|
-
return "night";
|
|
1095
|
-
}
|
|
1096
|
-
function buildSingleDailySlot(minuteOfDay, durationMinutes = 45) {
|
|
1097
|
-
return {
|
|
1098
|
-
key: `time-${minuteOfDay}`,
|
|
1099
|
-
label: formatMinuteOfDayLabel(minuteOfDay),
|
|
1100
|
-
minuteOfDay,
|
|
1101
|
-
durationMinutes,
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
function addYearsToLocalDate(dateOnly, yearDelta) {
|
|
1105
|
-
const utcDate = new Date(Date.UTC(dateOnly.year + yearDelta, dateOnly.month - 1, dateOnly.day, 12));
|
|
1106
|
-
return {
|
|
1107
|
-
year: utcDate.getUTCFullYear(),
|
|
1108
|
-
month: utcDate.getUTCMonth() + 1,
|
|
1109
|
-
day: utcDate.getUTCDate(),
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
function buildCustomTimeWindowPolicy(minuteOfDay, timeZone) {
|
|
1113
|
-
const basePolicy = resolveDefaultWindowPolicy(timeZone);
|
|
1114
|
-
return {
|
|
1115
|
-
timezone: basePolicy.timezone,
|
|
1116
|
-
windows: [
|
|
1117
|
-
...basePolicy.windows,
|
|
1118
|
-
{
|
|
1119
|
-
name: "custom",
|
|
1120
|
-
label: formatMinuteOfDayLabel(minuteOfDay),
|
|
1121
|
-
startMinute: minuteOfDay,
|
|
1122
|
-
endMinute: Math.min(minuteOfDay + 1, 24 * 60),
|
|
1123
|
-
},
|
|
1124
|
-
],
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
function formatMinuteOfDayLabel(minuteOfDay) {
|
|
1128
|
-
const hour24 = Math.floor(minuteOfDay / 60);
|
|
1129
|
-
const minute = minuteOfDay % 60;
|
|
1130
|
-
const meridiem = hour24 >= 12 ? "pm" : "am";
|
|
1131
|
-
const hour12 = hour24 % 12 === 0 ? 12 : hour24 % 12;
|
|
1132
|
-
return minute === 0
|
|
1133
|
-
? `${hour12}${meridiem}`
|
|
1134
|
-
: `${hour12}:${String(minute).padStart(2, "0")}${meridiem}`;
|
|
1135
|
-
}
|
|
1136
|
-
function parseClockToken(token) {
|
|
1137
|
-
const normalized = token.trim().toLowerCase();
|
|
1138
|
-
if (normalized === "noon") {
|
|
1139
|
-
return 12 * 60;
|
|
1140
|
-
}
|
|
1141
|
-
if (normalized === "midnight") {
|
|
1142
|
-
return 0;
|
|
1143
|
-
}
|
|
1144
|
-
const match = normalized.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/);
|
|
1145
|
-
if (!match) {
|
|
1146
|
-
return null;
|
|
1147
|
-
}
|
|
1148
|
-
const hour = Number(match[1]);
|
|
1149
|
-
const minute = Number(match[2] ?? "0");
|
|
1150
|
-
if (!Number.isFinite(hour) || !Number.isFinite(minute) || minute >= 60) {
|
|
1151
|
-
return null;
|
|
1152
|
-
}
|
|
1153
|
-
if (hour < 1 || hour > 12) {
|
|
1154
|
-
return null;
|
|
1155
|
-
}
|
|
1156
|
-
const meridiem = match[3];
|
|
1157
|
-
const normalizedHour = meridiem === "am" ? hour % 12 : hour % 12 === 0 ? 12 : (hour % 12) + 12;
|
|
1158
|
-
return normalizedHour * 60 + minute;
|
|
1159
|
-
}
|
|
1160
|
-
function parseTimeOfDayToken(token) {
|
|
1161
|
-
const normalized = normalizeLifeInputText(token).toLowerCase();
|
|
1162
|
-
const hhmmMatch = normalized.match(/^(\d{1,2}):(\d{2})$/);
|
|
1163
|
-
if (hhmmMatch) {
|
|
1164
|
-
const hour = Number(hhmmMatch[1]);
|
|
1165
|
-
const minute = Number(hhmmMatch[2]);
|
|
1166
|
-
if (Number.isFinite(hour) &&
|
|
1167
|
-
Number.isFinite(minute) &&
|
|
1168
|
-
hour >= 0 &&
|
|
1169
|
-
hour <= 23 &&
|
|
1170
|
-
minute >= 0 &&
|
|
1171
|
-
minute < 60) {
|
|
1172
|
-
return hour * 60 + minute;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
return parseClockToken(normalized);
|
|
1176
|
-
}
|
|
1177
|
-
function resolveAlarmDayOffset(intent) {
|
|
1178
|
-
const lower = normalizeLifeInputText(intent).toLowerCase();
|
|
1179
|
-
if (/\btomorrow\b/.test(lower))
|
|
1180
|
-
return 1;
|
|
1181
|
-
if (/\b(today|tonight)\b/.test(lower))
|
|
1182
|
-
return 0;
|
|
1183
|
-
return null;
|
|
1184
|
-
}
|
|
1185
|
-
function buildOneOffDueAtFromMinuteOfDay(args) {
|
|
1186
|
-
const now = args.now ?? new Date();
|
|
1187
|
-
const timeZone = args.timeZone ?? resolveDefaultTimeZone();
|
|
1188
|
-
const nowParts = getZonedDateParts(now, timeZone);
|
|
1189
|
-
let localDate = {
|
|
1190
|
-
year: nowParts.year,
|
|
1191
|
-
month: nowParts.month,
|
|
1192
|
-
day: nowParts.day,
|
|
1193
|
-
};
|
|
1194
|
-
const explicitDate = typeof args.intent === "string"
|
|
1195
|
-
? parseExplicitLocalDateForLifeRequest(args.intent, timeZone, now)
|
|
1196
|
-
: null;
|
|
1197
|
-
if (explicitDate) {
|
|
1198
|
-
localDate = explicitDate;
|
|
1199
|
-
}
|
|
1200
|
-
const explicitDayOffset = typeof args.intent === "string" ? resolveAlarmDayOffset(args.intent) : null;
|
|
1201
|
-
if (explicitDate === null && explicitDayOffset !== null) {
|
|
1202
|
-
localDate = addDaysToLocalDate(localDate, explicitDayOffset);
|
|
1203
|
-
}
|
|
1204
|
-
const buildCandidate = () => buildUtcDateFromLocalParts(timeZone, {
|
|
1205
|
-
...localDate,
|
|
1206
|
-
hour: Math.floor(args.minuteOfDay / 60),
|
|
1207
|
-
minute: args.minuteOfDay % 60,
|
|
1208
|
-
second: 0,
|
|
1209
|
-
});
|
|
1210
|
-
let candidate = buildCandidate();
|
|
1211
|
-
if (candidate.getTime() <= now.getTime()) {
|
|
1212
|
-
if (explicitDate && !explicitDate.explicitYear) {
|
|
1213
|
-
localDate = addYearsToLocalDate(localDate, 1);
|
|
1214
|
-
candidate = buildCandidate();
|
|
1215
|
-
}
|
|
1216
|
-
else if (explicitDate === null && explicitDayOffset === null) {
|
|
1217
|
-
localDate = addDaysToLocalDate(localDate, 1);
|
|
1218
|
-
candidate = buildCandidate();
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
return candidate.toISOString();
|
|
1222
|
-
}
|
|
1223
|
-
function parseExplicitLocalDateForLifeRequest(value, timeZone, now = new Date()) {
|
|
1224
|
-
const normalized = normalizeLifeInputText(value).toLowerCase();
|
|
1225
|
-
const localToday = getZonedDateParts(now, timeZone);
|
|
1226
|
-
const monthMap = {
|
|
1227
|
-
january: 1,
|
|
1228
|
-
jan: 1,
|
|
1229
|
-
february: 2,
|
|
1230
|
-
feb: 2,
|
|
1231
|
-
march: 3,
|
|
1232
|
-
mar: 3,
|
|
1233
|
-
april: 4,
|
|
1234
|
-
apr: 4,
|
|
1235
|
-
may: 5,
|
|
1236
|
-
june: 6,
|
|
1237
|
-
jun: 6,
|
|
1238
|
-
july: 7,
|
|
1239
|
-
jul: 7,
|
|
1240
|
-
august: 8,
|
|
1241
|
-
aug: 8,
|
|
1242
|
-
september: 9,
|
|
1243
|
-
sept: 9,
|
|
1244
|
-
sep: 9,
|
|
1245
|
-
october: 10,
|
|
1246
|
-
oct: 10,
|
|
1247
|
-
november: 11,
|
|
1248
|
-
nov: 11,
|
|
1249
|
-
december: 12,
|
|
1250
|
-
dec: 12,
|
|
1251
|
-
};
|
|
1252
|
-
const weekdayMap = {
|
|
1253
|
-
sunday: 0,
|
|
1254
|
-
sun: 0,
|
|
1255
|
-
monday: 1,
|
|
1256
|
-
mon: 1,
|
|
1257
|
-
tuesday: 2,
|
|
1258
|
-
tue: 2,
|
|
1259
|
-
tues: 2,
|
|
1260
|
-
wednesday: 3,
|
|
1261
|
-
wed: 3,
|
|
1262
|
-
thursday: 4,
|
|
1263
|
-
thu: 4,
|
|
1264
|
-
thur: 4,
|
|
1265
|
-
thurs: 4,
|
|
1266
|
-
friday: 5,
|
|
1267
|
-
fri: 5,
|
|
1268
|
-
saturday: 6,
|
|
1269
|
-
sat: 6,
|
|
1270
|
-
};
|
|
1271
|
-
const isoMatch = normalized.match(/\b(\d{4})-(\d{1,2})-(\d{1,2})\b/);
|
|
1272
|
-
if (isoMatch) {
|
|
1273
|
-
return {
|
|
1274
|
-
year: Number(isoMatch[1]),
|
|
1275
|
-
month: Number(isoMatch[2]),
|
|
1276
|
-
day: Number(isoMatch[3]),
|
|
1277
|
-
explicitYear: true,
|
|
1278
|
-
};
|
|
1279
|
-
}
|
|
1280
|
-
const monthNameMatch = normalized.match(/\b(jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\.?\s+(\d{1,2})(?:st|nd|rd|th)?(?:,?\s+(\d{4}))?\b/i);
|
|
1281
|
-
if (monthNameMatch) {
|
|
1282
|
-
return {
|
|
1283
|
-
year: monthNameMatch[3] ? Number(monthNameMatch[3]) : localToday.year,
|
|
1284
|
-
month: monthMap[monthNameMatch[1].toLowerCase().replace(/\./g, "")],
|
|
1285
|
-
day: Number(monthNameMatch[2]),
|
|
1286
|
-
explicitYear: Boolean(monthNameMatch[3]),
|
|
1287
|
-
};
|
|
1288
|
-
}
|
|
1289
|
-
const numericMatch = normalized.match(/\b(\d{1,2})[/-](\d{1,2})(?:[/-](\d{2,4}))?\b/);
|
|
1290
|
-
if (numericMatch) {
|
|
1291
|
-
const yearRaw = numericMatch[3];
|
|
1292
|
-
const year = yearRaw === undefined
|
|
1293
|
-
? localToday.year
|
|
1294
|
-
: yearRaw.length === 2
|
|
1295
|
-
? 2000 + Number(yearRaw)
|
|
1296
|
-
: Number(yearRaw);
|
|
1297
|
-
return {
|
|
1298
|
-
year,
|
|
1299
|
-
month: Number(numericMatch[1]),
|
|
1300
|
-
day: Number(numericMatch[2]),
|
|
1301
|
-
explicitYear: Boolean(yearRaw),
|
|
1302
|
-
};
|
|
1303
|
-
}
|
|
1304
|
-
const weekdayMatch = normalized.match(/\b(?:(this|next)\s+)?(sun(?:day)?|mon(?:day)?|tue(?:s(?:day)?)?|wed(?:nesday)?|thu(?:r(?:s(?:day)?)?)?|fri(?:day)?|sat(?:urday)?)\b/i);
|
|
1305
|
-
if (!weekdayMatch) {
|
|
1306
|
-
return null;
|
|
1307
|
-
}
|
|
1308
|
-
const weekdayToken = weekdayMatch[2]?.toLowerCase();
|
|
1309
|
-
const targetWeekday = weekdayToken ? weekdayMap[weekdayToken] : undefined;
|
|
1310
|
-
if (targetWeekday === undefined) {
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
const qualifier = weekdayMatch[1]?.toLowerCase() ?? "";
|
|
1314
|
-
const currentWeekday = new Date(Date.UTC(localToday.year, Math.max(0, localToday.month - 1), localToday.day, 12)).getUTCDay();
|
|
1315
|
-
let delta = (targetWeekday - currentWeekday + 7) % 7;
|
|
1316
|
-
if (qualifier === "next") {
|
|
1317
|
-
delta = delta === 0 ? 7 : delta + 7;
|
|
1318
|
-
}
|
|
1319
|
-
const resolved = addDaysToLocalDate({
|
|
1320
|
-
year: localToday.year,
|
|
1321
|
-
month: localToday.month,
|
|
1322
|
-
day: localToday.day,
|
|
1323
|
-
}, delta);
|
|
1324
|
-
return {
|
|
1325
|
-
...resolved,
|
|
1326
|
-
explicitYear: false,
|
|
1327
|
-
};
|
|
1328
|
-
}
|
|
1329
|
-
function mergeMetadataRecords(...records) {
|
|
1330
|
-
const merged = Object.assign({}, ...records.filter((record) => record != null && Object.keys(record).length > 0));
|
|
1331
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
1332
|
-
}
|
|
1333
|
-
function extractExplicitDailySlots(intent) {
|
|
1334
|
-
const tokens = [
|
|
1335
|
-
...intent.matchAll(/\b(\d{1,2}(?::\d{2})?\s*(?:am|pm)|noon|midnight)\b/gi),
|
|
1336
|
-
].map((match) => match[1]);
|
|
1337
|
-
const seen = new Set();
|
|
1338
|
-
const slots = [];
|
|
1339
|
-
for (const [index, token] of tokens.entries()) {
|
|
1340
|
-
const minuteOfDay = parseClockToken(token);
|
|
1341
|
-
if (minuteOfDay === null || seen.has(minuteOfDay)) {
|
|
1342
|
-
continue;
|
|
1343
|
-
}
|
|
1344
|
-
seen.add(minuteOfDay);
|
|
1345
|
-
slots.push({
|
|
1346
|
-
key: `clock-${index + 1}`,
|
|
1347
|
-
label: token.trim(),
|
|
1348
|
-
minuteOfDay,
|
|
1349
|
-
durationMinutes: 45,
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
return slots.sort((left, right) => left.minuteOfDay - right.minuteOfDay);
|
|
1353
|
-
}
|
|
1354
|
-
function normalizeLifeWindows(value) {
|
|
1355
|
-
const values = Array.isArray(value) ? value : value == null ? [] : [value];
|
|
1356
|
-
const normalized = values.flatMap((entry) => {
|
|
1357
|
-
if (typeof entry !== "string") {
|
|
1358
|
-
return [];
|
|
1359
|
-
}
|
|
1360
|
-
const lower = normalizeLifeInputText(entry).toLowerCase();
|
|
1361
|
-
if (lower === "morning")
|
|
1362
|
-
return ["morning"];
|
|
1363
|
-
if (lower === "afternoon")
|
|
1364
|
-
return ["afternoon"];
|
|
1365
|
-
if (lower === "evening")
|
|
1366
|
-
return ["evening"];
|
|
1367
|
-
if (lower === "night")
|
|
1368
|
-
return ["night"];
|
|
1369
|
-
return [];
|
|
1370
|
-
});
|
|
1371
|
-
return [...new Set(normalized)];
|
|
1372
|
-
}
|
|
1373
|
-
function normalizeCadenceDetail(value) {
|
|
1374
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1375
|
-
return undefined;
|
|
1376
|
-
}
|
|
1377
|
-
const record = value;
|
|
1378
|
-
const cadenceKind = typeof record.kind === "string"
|
|
1379
|
-
? normalizeLifeInputText(record.kind).toLowerCase()
|
|
1380
|
-
: typeof record.type === "string"
|
|
1381
|
-
? normalizeLifeInputText(record.type).toLowerCase()
|
|
1382
|
-
: "";
|
|
1383
|
-
if (!cadenceKind) {
|
|
1384
|
-
return undefined;
|
|
1385
|
-
}
|
|
1386
|
-
if (cadenceKind === "once" && typeof record.dueAt === "string") {
|
|
1387
|
-
return {
|
|
1388
|
-
kind: "once",
|
|
1389
|
-
dueAt: record.dueAt,
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
if (cadenceKind === "interval") {
|
|
1393
|
-
const everyMinutes = typeof record.everyMinutes === "number"
|
|
1394
|
-
? record.everyMinutes
|
|
1395
|
-
: typeof record.everyMinutes === "string"
|
|
1396
|
-
? Number(record.everyMinutes)
|
|
1397
|
-
: typeof record.minutes === "number"
|
|
1398
|
-
? record.minutes
|
|
1399
|
-
: typeof record.minutes === "string"
|
|
1400
|
-
? Number(record.minutes)
|
|
1401
|
-
: NaN;
|
|
1402
|
-
if (Number.isFinite(everyMinutes) && everyMinutes > 0) {
|
|
1403
|
-
return {
|
|
1404
|
-
kind: "interval",
|
|
1405
|
-
everyMinutes,
|
|
1406
|
-
windows: normalizeLifeWindows(record.windows),
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1409
|
-
return undefined;
|
|
1410
|
-
}
|
|
1411
|
-
if (cadenceKind === "weekly") {
|
|
1412
|
-
const weekdays = Array.isArray(record.weekdays)
|
|
1413
|
-
? record.weekdays
|
|
1414
|
-
.map((entry) => typeof entry === "number"
|
|
1415
|
-
? entry
|
|
1416
|
-
: typeof entry === "string"
|
|
1417
|
-
? Number(entry)
|
|
1418
|
-
: NaN)
|
|
1419
|
-
.filter((entry) => Number.isFinite(entry))
|
|
1420
|
-
: [];
|
|
1421
|
-
if (weekdays.length > 0) {
|
|
1422
|
-
return {
|
|
1423
|
-
kind: "weekly",
|
|
1424
|
-
weekdays,
|
|
1425
|
-
windows: normalizeLifeWindows(record.windows),
|
|
1426
|
-
};
|
|
1427
|
-
}
|
|
1428
|
-
return undefined;
|
|
1429
|
-
}
|
|
1430
|
-
const explicitTimes = Array.isArray(record.times)
|
|
1431
|
-
? record.times
|
|
1432
|
-
.map((entry) => typeof entry === "string" ? parseTimeOfDayToken(entry) : null)
|
|
1433
|
-
.filter((entry) => entry !== null)
|
|
1434
|
-
: [];
|
|
1435
|
-
if (explicitTimes.length > 0) {
|
|
1436
|
-
return {
|
|
1437
|
-
kind: "times_per_day",
|
|
1438
|
-
slots: explicitTimes.map((minuteOfDay, index) => ({
|
|
1439
|
-
key: `time-${index + 1}`,
|
|
1440
|
-
label: formatMinuteOfDayLabel(minuteOfDay),
|
|
1441
|
-
minuteOfDay,
|
|
1442
|
-
durationMinutes: 45,
|
|
1443
|
-
})),
|
|
1444
|
-
visibilityLeadMinutes: 90,
|
|
1445
|
-
visibilityLagMinutes: 180,
|
|
1446
|
-
};
|
|
1447
|
-
}
|
|
1448
|
-
if (cadenceKind === "times_per_day") {
|
|
1449
|
-
if (Array.isArray(record.slots)) {
|
|
1450
|
-
const slots = record.slots
|
|
1451
|
-
.map((entry, index) => {
|
|
1452
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
1453
|
-
return null;
|
|
1454
|
-
}
|
|
1455
|
-
const slot = entry;
|
|
1456
|
-
const minuteOfDay = typeof slot.minuteOfDay === "number"
|
|
1457
|
-
? slot.minuteOfDay
|
|
1458
|
-
: typeof slot.minuteOfDay === "string"
|
|
1459
|
-
? Number(slot.minuteOfDay)
|
|
1460
|
-
: null;
|
|
1461
|
-
if (minuteOfDay === null || !Number.isFinite(minuteOfDay)) {
|
|
1462
|
-
return null;
|
|
1463
|
-
}
|
|
1464
|
-
return {
|
|
1465
|
-
key: typeof slot.key === "string" && slot.key.trim().length > 0
|
|
1466
|
-
? slot.key
|
|
1467
|
-
: `slot-${index + 1}`,
|
|
1468
|
-
label: typeof slot.label === "string" && slot.label.trim().length > 0
|
|
1469
|
-
? slot.label
|
|
1470
|
-
: formatMinuteOfDayLabel(minuteOfDay),
|
|
1471
|
-
minuteOfDay,
|
|
1472
|
-
durationMinutes: typeof slot.durationMinutes === "number" &&
|
|
1473
|
-
Number.isFinite(slot.durationMinutes) &&
|
|
1474
|
-
slot.durationMinutes > 0
|
|
1475
|
-
? slot.durationMinutes
|
|
1476
|
-
: 45,
|
|
1477
|
-
};
|
|
1478
|
-
})
|
|
1479
|
-
.filter((entry) => entry !== null);
|
|
1480
|
-
if (slots.length > 0) {
|
|
1481
|
-
return {
|
|
1482
|
-
kind: "times_per_day",
|
|
1483
|
-
slots,
|
|
1484
|
-
visibilityLeadMinutes: typeof record.visibilityLeadMinutes === "number"
|
|
1485
|
-
? record.visibilityLeadMinutes
|
|
1486
|
-
: 90,
|
|
1487
|
-
visibilityLagMinutes: typeof record.visibilityLagMinutes === "number"
|
|
1488
|
-
? record.visibilityLagMinutes
|
|
1489
|
-
: 180,
|
|
1490
|
-
};
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
const count = typeof record.count === "number"
|
|
1494
|
-
? record.count
|
|
1495
|
-
: typeof record.count === "string"
|
|
1496
|
-
? Number(record.count)
|
|
1497
|
-
: NaN;
|
|
1498
|
-
if (Number.isFinite(count) && count > 0) {
|
|
1499
|
-
return {
|
|
1500
|
-
kind: "times_per_day",
|
|
1501
|
-
slots: buildDistributedDailySlots(count),
|
|
1502
|
-
visibilityLeadMinutes: 90,
|
|
1503
|
-
visibilityLagMinutes: 180,
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
if (cadenceKind === "daily") {
|
|
1508
|
-
const windows = normalizeLifeWindows(record.windows ?? record.window);
|
|
1509
|
-
if (windows.length > 0) {
|
|
1510
|
-
return {
|
|
1511
|
-
kind: "daily",
|
|
1512
|
-
windows,
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
return {
|
|
1516
|
-
kind: "daily",
|
|
1517
|
-
windows: ["morning"],
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
return undefined;
|
|
1521
|
-
}
|
|
1522
|
-
/**
|
|
1523
|
-
* Convert LLM-extracted params into a typed LifeOpsCadence.
|
|
1524
|
-
* Returns null when the LLM output is insufficient to construct a
|
|
1525
|
-
* valid cadence, letting the caller fall back to regex-derived values.
|
|
1526
|
-
*/
|
|
1527
|
-
function buildCadenceFromLlmParams(params, context) {
|
|
1528
|
-
const kind = params.cadenceKind;
|
|
1529
|
-
if (!kind)
|
|
1530
|
-
return null;
|
|
1531
|
-
const effectiveTimeZone = context?.timeZone;
|
|
1532
|
-
const timeOfDayMinute = typeof params.timeOfDay === "string"
|
|
1533
|
-
? parseTimeOfDayToken(params.timeOfDay)
|
|
1534
|
-
: null;
|
|
1535
|
-
const explicitSlots = typeof context?.intent === "string"
|
|
1536
|
-
? extractExplicitDailySlots(context.intent)
|
|
1537
|
-
: [];
|
|
1538
|
-
const slotDuration = typeof params.durationMinutes === "number" && params.durationMinutes > 0
|
|
1539
|
-
? params.durationMinutes
|
|
1540
|
-
: 45;
|
|
1541
|
-
const windows = (params.windows ?? []).filter((w) => w === "morning" || w === "afternoon" || w === "evening" || w === "night");
|
|
1542
|
-
const effectiveWindows = windows.length > 0
|
|
1543
|
-
? windows
|
|
1544
|
-
: timeOfDayMinute !== null
|
|
1545
|
-
? [inferWindowFromMinuteOfDay(timeOfDayMinute)]
|
|
1546
|
-
: ["morning"];
|
|
1547
|
-
if (kind === "once") {
|
|
1548
|
-
if (timeOfDayMinute !== null) {
|
|
1549
|
-
return {
|
|
1550
|
-
cadence: {
|
|
1551
|
-
kind: "once",
|
|
1552
|
-
dueAt: buildOneOffDueAtFromMinuteOfDay({
|
|
1553
|
-
intent: context?.intent,
|
|
1554
|
-
minuteOfDay: timeOfDayMinute,
|
|
1555
|
-
now: context?.now,
|
|
1556
|
-
timeZone: effectiveTimeZone,
|
|
1557
|
-
}),
|
|
1558
|
-
},
|
|
1559
|
-
};
|
|
1560
|
-
}
|
|
1561
|
-
return { cadence: { kind: "once", dueAt: new Date().toISOString() } };
|
|
1562
|
-
}
|
|
1563
|
-
if (kind === "daily") {
|
|
1564
|
-
if (explicitSlots.length >= 2) {
|
|
1565
|
-
return {
|
|
1566
|
-
cadence: {
|
|
1567
|
-
kind: "times_per_day",
|
|
1568
|
-
slots: explicitSlots.map((slot) => ({
|
|
1569
|
-
...slot,
|
|
1570
|
-
durationMinutes: slot.durationMinutes ?? slotDuration,
|
|
1571
|
-
})),
|
|
1572
|
-
visibilityLeadMinutes: 90,
|
|
1573
|
-
visibilityLagMinutes: 180,
|
|
1574
|
-
},
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
if (timeOfDayMinute !== null) {
|
|
1578
|
-
return {
|
|
1579
|
-
cadence: {
|
|
1580
|
-
kind: "times_per_day",
|
|
1581
|
-
slots: [buildSingleDailySlot(timeOfDayMinute, slotDuration)],
|
|
1582
|
-
visibilityLeadMinutes: 90,
|
|
1583
|
-
visibilityLagMinutes: 180,
|
|
1584
|
-
},
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
if (effectiveWindows.length >= 2) {
|
|
1588
|
-
return {
|
|
1589
|
-
cadence: {
|
|
1590
|
-
kind: "times_per_day",
|
|
1591
|
-
slots: buildSlotsFromWindows(effectiveWindows),
|
|
1592
|
-
visibilityLeadMinutes: 90,
|
|
1593
|
-
visibilityLagMinutes: 180,
|
|
1594
|
-
},
|
|
1595
|
-
};
|
|
1596
|
-
}
|
|
1597
|
-
return { cadence: { kind: "daily", windows: effectiveWindows } };
|
|
1598
|
-
}
|
|
1599
|
-
if (kind === "weekly") {
|
|
1600
|
-
const weekdays = params.weekdays;
|
|
1601
|
-
if (!weekdays || weekdays.length === 0)
|
|
1602
|
-
return null;
|
|
1603
|
-
if (timeOfDayMinute !== null) {
|
|
1604
|
-
return {
|
|
1605
|
-
cadence: { kind: "weekly", weekdays, windows: ["custom"] },
|
|
1606
|
-
windowPolicy: buildCustomTimeWindowPolicy(timeOfDayMinute, effectiveTimeZone ?? resolveDefaultTimeZone()),
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
return { cadence: { kind: "weekly", weekdays, windows: effectiveWindows } };
|
|
1610
|
-
}
|
|
1611
|
-
if (kind === "interval") {
|
|
1612
|
-
const everyMinutes = params.everyMinutes;
|
|
1613
|
-
if (!everyMinutes || everyMinutes <= 0)
|
|
1614
|
-
return null;
|
|
1615
|
-
return {
|
|
1616
|
-
cadence: {
|
|
1617
|
-
kind: "interval",
|
|
1618
|
-
everyMinutes,
|
|
1619
|
-
windows: effectiveWindows,
|
|
1620
|
-
startMinuteOfDay: timeOfDayMinute ?? undefined,
|
|
1621
|
-
durationMinutes: typeof params.durationMinutes === "number" &&
|
|
1622
|
-
params.durationMinutes > 0
|
|
1623
|
-
? params.durationMinutes
|
|
1624
|
-
: undefined,
|
|
1625
|
-
},
|
|
1626
|
-
};
|
|
1627
|
-
}
|
|
1628
|
-
if (kind === "times_per_day") {
|
|
1629
|
-
if (explicitSlots.length >= 2) {
|
|
1630
|
-
return {
|
|
1631
|
-
cadence: {
|
|
1632
|
-
kind: "times_per_day",
|
|
1633
|
-
slots: explicitSlots.map((slot) => ({
|
|
1634
|
-
...slot,
|
|
1635
|
-
durationMinutes: slot.durationMinutes ?? slotDuration,
|
|
1636
|
-
})),
|
|
1637
|
-
visibilityLeadMinutes: 90,
|
|
1638
|
-
visibilityLagMinutes: 180,
|
|
1639
|
-
},
|
|
1640
|
-
};
|
|
1641
|
-
}
|
|
1642
|
-
if (timeOfDayMinute !== null) {
|
|
1643
|
-
return {
|
|
1644
|
-
cadence: {
|
|
1645
|
-
kind: "times_per_day",
|
|
1646
|
-
slots: [buildSingleDailySlot(timeOfDayMinute, slotDuration)],
|
|
1647
|
-
visibilityLeadMinutes: 90,
|
|
1648
|
-
visibilityLagMinutes: 180,
|
|
1649
|
-
},
|
|
1650
|
-
};
|
|
1651
|
-
}
|
|
1652
|
-
const count = params.timesPerDay;
|
|
1653
|
-
if (!count || count <= 0)
|
|
1654
|
-
return null;
|
|
1655
|
-
return {
|
|
1656
|
-
cadence: {
|
|
1657
|
-
kind: "times_per_day",
|
|
1658
|
-
slots: buildDistributedDailySlots(count).map((slot) => ({
|
|
1659
|
-
...slot,
|
|
1660
|
-
durationMinutes: slotDuration,
|
|
1661
|
-
})),
|
|
1662
|
-
visibilityLeadMinutes: 90,
|
|
1663
|
-
visibilityLagMinutes: 180,
|
|
1664
|
-
},
|
|
1665
|
-
};
|
|
1666
|
-
}
|
|
1667
|
-
return null;
|
|
1668
|
-
}
|
|
1669
|
-
function buildCadenceFromUpdateFields(args) {
|
|
1670
|
-
const { currentCadence, currentWindowPolicy, timeZone, update } = args;
|
|
1671
|
-
const kind = (update.cadenceKind ??
|
|
1672
|
-
currentCadence.kind);
|
|
1673
|
-
const requestedWindows = normalizeLifeWindows(update.windows ?? []);
|
|
1674
|
-
const timeOfDayMinute = typeof update.timeOfDay === "string"
|
|
1675
|
-
? parseTimeOfDayToken(update.timeOfDay)
|
|
1676
|
-
: null;
|
|
1677
|
-
if (kind === "interval") {
|
|
1678
|
-
const everyMinutes = update.everyMinutes ??
|
|
1679
|
-
(currentCadence.kind === "interval" ? currentCadence.everyMinutes : null);
|
|
1680
|
-
if (!everyMinutes || everyMinutes <= 0) {
|
|
1681
|
-
return null;
|
|
1682
|
-
}
|
|
1683
|
-
const windows = requestedWindows.length > 0
|
|
1684
|
-
? requestedWindows
|
|
1685
|
-
: currentCadence.kind === "interval" &&
|
|
1686
|
-
currentCadence.windows.length > 0
|
|
1687
|
-
? normalizeLifeWindows(currentCadence.windows)
|
|
1688
|
-
: timeOfDayMinute !== null
|
|
1689
|
-
? [inferWindowFromMinuteOfDay(timeOfDayMinute)]
|
|
1690
|
-
: ["morning"];
|
|
1691
|
-
return {
|
|
1692
|
-
cadence: {
|
|
1693
|
-
kind: "interval",
|
|
1694
|
-
everyMinutes,
|
|
1695
|
-
windows,
|
|
1696
|
-
startMinuteOfDay: timeOfDayMinute ??
|
|
1697
|
-
(currentCadence.kind === "interval"
|
|
1698
|
-
? currentCadence.startMinuteOfDay
|
|
1699
|
-
: undefined),
|
|
1700
|
-
maxOccurrencesPerDay: currentCadence.kind === "interval"
|
|
1701
|
-
? currentCadence.maxOccurrencesPerDay
|
|
1702
|
-
: undefined,
|
|
1703
|
-
durationMinutes: currentCadence.kind === "interval"
|
|
1704
|
-
? currentCadence.durationMinutes
|
|
1705
|
-
: undefined,
|
|
1706
|
-
visibilityLeadMinutes: currentCadence.kind === "interval"
|
|
1707
|
-
? currentCadence.visibilityLeadMinutes
|
|
1708
|
-
: undefined,
|
|
1709
|
-
visibilityLagMinutes: currentCadence.kind === "interval"
|
|
1710
|
-
? currentCadence.visibilityLagMinutes
|
|
1711
|
-
: undefined,
|
|
1712
|
-
},
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
if (kind === "weekly") {
|
|
1716
|
-
const weekdays = update.weekdays ??
|
|
1717
|
-
(currentCadence.kind === "weekly" ? currentCadence.weekdays : null);
|
|
1718
|
-
if (!weekdays || weekdays.length === 0) {
|
|
1719
|
-
return null;
|
|
1720
|
-
}
|
|
1721
|
-
if (timeOfDayMinute !== null) {
|
|
1722
|
-
return {
|
|
1723
|
-
cadence: {
|
|
1724
|
-
kind: "weekly",
|
|
1725
|
-
weekdays,
|
|
1726
|
-
windows: ["custom"],
|
|
1727
|
-
visibilityLeadMinutes: currentCadence.kind === "weekly"
|
|
1728
|
-
? currentCadence.visibilityLeadMinutes
|
|
1729
|
-
: undefined,
|
|
1730
|
-
visibilityLagMinutes: currentCadence.kind === "weekly"
|
|
1731
|
-
? currentCadence.visibilityLagMinutes
|
|
1732
|
-
: undefined,
|
|
1733
|
-
},
|
|
1734
|
-
windowPolicy: buildCustomTimeWindowPolicy(timeOfDayMinute, timeZone),
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
return {
|
|
1738
|
-
cadence: {
|
|
1739
|
-
kind: "weekly",
|
|
1740
|
-
weekdays,
|
|
1741
|
-
windows: requestedWindows.length > 0
|
|
1742
|
-
? requestedWindows
|
|
1743
|
-
: currentCadence.kind === "weekly" &&
|
|
1744
|
-
currentCadence.windows.length > 0
|
|
1745
|
-
? currentCadence.windows
|
|
1746
|
-
: ["morning"],
|
|
1747
|
-
visibilityLeadMinutes: currentCadence.kind === "weekly"
|
|
1748
|
-
? currentCadence.visibilityLeadMinutes
|
|
1749
|
-
: undefined,
|
|
1750
|
-
visibilityLagMinutes: currentCadence.kind === "weekly"
|
|
1751
|
-
? currentCadence.visibilityLagMinutes
|
|
1752
|
-
: undefined,
|
|
1753
|
-
},
|
|
1754
|
-
windowPolicy: currentWindowPolicy.windows.some((window) => (requestedWindows.length > 0
|
|
1755
|
-
? requestedWindows
|
|
1756
|
-
: ["morning"]).includes(window.name))
|
|
1757
|
-
? undefined
|
|
1758
|
-
: resolveDefaultWindowPolicy(timeZone),
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
|
-
if (kind === "daily") {
|
|
1762
|
-
if (timeOfDayMinute !== null) {
|
|
1763
|
-
return {
|
|
1764
|
-
cadence: {
|
|
1765
|
-
kind: "times_per_day",
|
|
1766
|
-
slots: [buildSingleDailySlot(timeOfDayMinute)],
|
|
1767
|
-
visibilityLeadMinutes: 90,
|
|
1768
|
-
visibilityLagMinutes: 180,
|
|
1769
|
-
},
|
|
1770
|
-
};
|
|
1771
|
-
}
|
|
1772
|
-
return {
|
|
1773
|
-
cadence: {
|
|
1774
|
-
kind: "daily",
|
|
1775
|
-
windows: requestedWindows.length > 0
|
|
1776
|
-
? requestedWindows
|
|
1777
|
-
: currentCadence.kind === "daily" &&
|
|
1778
|
-
currentCadence.windows.length > 0
|
|
1779
|
-
? currentCadence.windows
|
|
1780
|
-
: ["morning"],
|
|
1781
|
-
visibilityLeadMinutes: currentCadence.kind === "daily"
|
|
1782
|
-
? currentCadence.visibilityLeadMinutes
|
|
1783
|
-
: undefined,
|
|
1784
|
-
visibilityLagMinutes: currentCadence.kind === "daily"
|
|
1785
|
-
? currentCadence.visibilityLagMinutes
|
|
1786
|
-
: undefined,
|
|
1787
|
-
},
|
|
1788
|
-
};
|
|
1789
|
-
}
|
|
1790
|
-
if (kind === "times_per_day") {
|
|
1791
|
-
if (timeOfDayMinute !== null) {
|
|
1792
|
-
return {
|
|
1793
|
-
cadence: {
|
|
1794
|
-
kind: "times_per_day",
|
|
1795
|
-
slots: [buildSingleDailySlot(timeOfDayMinute)],
|
|
1796
|
-
visibilityLeadMinutes: 90,
|
|
1797
|
-
visibilityLagMinutes: 180,
|
|
1798
|
-
},
|
|
1799
|
-
};
|
|
1800
|
-
}
|
|
1801
|
-
if (requestedWindows.length > 0) {
|
|
1802
|
-
return {
|
|
1803
|
-
cadence: {
|
|
1804
|
-
kind: "times_per_day",
|
|
1805
|
-
slots: buildSlotsFromWindows(requestedWindows),
|
|
1806
|
-
visibilityLeadMinutes: 90,
|
|
1807
|
-
visibilityLagMinutes: 180,
|
|
1808
|
-
},
|
|
1809
|
-
};
|
|
1810
|
-
}
|
|
1811
|
-
return currentCadence.kind === "times_per_day"
|
|
1812
|
-
? { cadence: currentCadence }
|
|
1813
|
-
: null;
|
|
1814
|
-
}
|
|
1815
|
-
return currentCadence.kind === "once" ? { cadence: currentCadence } : null;
|
|
1816
|
-
}
|
|
1817
|
-
function hasDefinitionUpdateChanges(request) {
|
|
1818
|
-
return (request.title != null ||
|
|
1819
|
-
request.cadence != null ||
|
|
1820
|
-
request.priority != null ||
|
|
1821
|
-
request.description != null ||
|
|
1822
|
-
request.windowPolicy != null ||
|
|
1823
|
-
request.reminderPlan != null);
|
|
1824
|
-
}
|
|
1825
|
-
function buildDefaultReminderPlan(label) {
|
|
1826
|
-
return {
|
|
1827
|
-
steps: [{ channel: "in_app", offsetMinutes: 0, label }],
|
|
1828
|
-
};
|
|
1829
|
-
}
|
|
1830
|
-
function scoreDefinitionTitleQuality(value) {
|
|
1831
|
-
const normalized = normalizeTitle(value ?? "");
|
|
1832
|
-
if (!normalized) {
|
|
1833
|
-
return Number.NEGATIVE_INFINITY;
|
|
1834
|
-
}
|
|
1835
|
-
let score = normalized.split(/\s+/).filter(Boolean).length;
|
|
1836
|
-
if (/\b\d+\b/.test(normalized)) {
|
|
1837
|
-
score += 6;
|
|
1838
|
-
}
|
|
1839
|
-
if (/[+&]/.test(value ?? "") || /\band\b/.test(normalized)) {
|
|
1840
|
-
score += 4;
|
|
1841
|
-
}
|
|
1842
|
-
if (/^(?:do|work out|workout|habit|routine|task|todo|reminder|alarm)\b/.test(normalized)) {
|
|
1843
|
-
score -= 5;
|
|
1844
|
-
}
|
|
1845
|
-
if (GENERIC_DERIVED_TITLE_RE.test(normalized)) {
|
|
1846
|
-
score -= 6;
|
|
1847
|
-
}
|
|
1848
|
-
return score;
|
|
1849
|
-
}
|
|
1850
|
-
function shouldAdoptPlannerTitle(args) {
|
|
1851
|
-
const plannerTitle = args.plannerTitle?.trim();
|
|
1852
|
-
if (!plannerTitle) {
|
|
1853
|
-
return false;
|
|
1854
|
-
}
|
|
1855
|
-
const currentTitle = args.currentTitle?.trim();
|
|
1856
|
-
if (!currentTitle) {
|
|
1857
|
-
return true;
|
|
1858
|
-
}
|
|
1859
|
-
if (normalizeTitle(currentTitle) === normalizeTitle(plannerTitle)) {
|
|
1860
|
-
return false;
|
|
1861
|
-
}
|
|
1862
|
-
return (scoreDefinitionTitleQuality(plannerTitle) >
|
|
1863
|
-
scoreDefinitionTitleQuality(currentTitle));
|
|
1864
|
-
}
|
|
1865
|
-
function shouldAdoptPlannerCadence(args) {
|
|
1866
|
-
const { currentCadence, plannerCadence } = args;
|
|
1867
|
-
if (!currentCadence) {
|
|
1868
|
-
return true;
|
|
1869
|
-
}
|
|
1870
|
-
if (currentCadence.kind === "times_per_day") {
|
|
1871
|
-
return ((plannerCadence.kind === "times_per_day" &&
|
|
1872
|
-
plannerCadence.slots.length >= currentCadence.slots.length) ||
|
|
1873
|
-
(plannerCadence.kind === "once" && currentCadence.slots.length === 1));
|
|
1874
|
-
}
|
|
1875
|
-
if (currentCadence.kind === "weekly") {
|
|
1876
|
-
return (plannerCadence.kind === "weekly" &&
|
|
1877
|
-
plannerCadence.weekdays.length >= currentCadence.weekdays.length &&
|
|
1878
|
-
(currentCadence.windows.includes("custom")
|
|
1879
|
-
? plannerCadence.windows.includes("custom")
|
|
1880
|
-
: plannerCadence.windows.length >= currentCadence.windows.length));
|
|
1881
|
-
}
|
|
1882
|
-
if (currentCadence.kind === "interval") {
|
|
1883
|
-
return plannerCadence.kind === "interval";
|
|
1884
|
-
}
|
|
1885
|
-
if (currentCadence.kind === "once") {
|
|
1886
|
-
return plannerCadence.kind === "once";
|
|
1887
|
-
}
|
|
1888
|
-
if (currentCadence.kind === "daily") {
|
|
1889
|
-
return (plannerCadence.kind === "times_per_day" ||
|
|
1890
|
-
(plannerCadence.kind === "daily" &&
|
|
1891
|
-
plannerCadence.windows.length >= currentCadence.windows.length));
|
|
1892
|
-
}
|
|
1893
|
-
return true;
|
|
1894
|
-
}
|
|
1895
|
-
function shouldRequireLifeCreateConfirmation(args) {
|
|
1896
|
-
if (args.messageSource === "autonomy") {
|
|
1897
|
-
return false;
|
|
1898
|
-
}
|
|
1899
|
-
if (args.requestKind && args.cadence?.kind === "once") {
|
|
1900
|
-
return false;
|
|
1901
|
-
}
|
|
1902
|
-
return !args.confirmed;
|
|
1903
|
-
}
|
|
1904
|
-
function describeReminderIntensity(intensity) {
|
|
1905
|
-
switch (intensity) {
|
|
1906
|
-
case "minimal":
|
|
1907
|
-
return "minimal";
|
|
1908
|
-
case "normal":
|
|
1909
|
-
return "normal";
|
|
1910
|
-
case "persistent":
|
|
1911
|
-
return "persistent";
|
|
1912
|
-
case "high_priority_only":
|
|
1913
|
-
return "high priority only";
|
|
1914
|
-
}
|
|
1915
|
-
return "normal";
|
|
1916
|
-
}
|
|
1917
|
-
// ── Main action ───────────────────────────────────────
|
|
1918
|
-
export const lifeAction = {
|
|
1919
|
-
name: "LIFE",
|
|
1920
|
-
similes: [
|
|
1921
|
-
"MANAGE_LIFEOPS",
|
|
1922
|
-
"QUERY_LIFEOPS",
|
|
1923
|
-
"CREATE_TASK",
|
|
1924
|
-
"CREATE_HABIT",
|
|
1925
|
-
"CREATE_GOAL",
|
|
1926
|
-
"TRACK_HABIT",
|
|
1927
|
-
"COMPLETE_TASK",
|
|
1928
|
-
"SET_ALARM",
|
|
1929
|
-
"SET_REMINDER",
|
|
1930
|
-
"SNOOZE_REMINDER",
|
|
1931
|
-
"SET_REMINDER_INTENSITY",
|
|
1932
|
-
],
|
|
1933
|
-
description: "Manage the user's personal routines, habits, goals, reminders, alarms, and escalation settings through LifeOps. " +
|
|
1934
|
-
"USE this action for: creating, editing, or deleting tasks, habits, routines, and goals; " +
|
|
1935
|
-
"setting one-off alarms or wake-up reminders like 'set an alarm for 7am' or 'wake me up at 7'; " +
|
|
1936
|
-
"helping the user actually set up follow-through when they say things like 'help me brush my teeth every day', 'i keep forgetting x', or 'help me actually do it'; " +
|
|
1937
|
-
"using LifeOps defaults for common routines when the user gives a natural window instead of an exact clock, like water reminders, stretch breaks, weekday-after-lunch Invisalign checks, or brushing when they wake up and before bed; " +
|
|
1938
|
-
"marking items as complete, skipping, or snoozing them; reviewing goal progress; " +
|
|
1939
|
-
"setting up phone/SMS escalation channels; adjusting reminder frequency or intensity; " +
|
|
1940
|
-
"querying an overview of active LifeOps items. " +
|
|
1941
|
-
"ALWAYS use LIFE for dynamic status questions like 'what's still left for today', 'what do i still need to do today', or 'anything else in my LifeOps list', even when the conversation already mentioned tasks, because their status may have changed after a completion, snooze, or reminder. " +
|
|
1942
|
-
"DO NOT use this action for Gmail inbox triage, email search, drafting or sending emails — use GMAIL_ACTION instead. " +
|
|
1943
|
-
"DO NOT use this action for calendar lookups, scheduling meetings, searching events, or travel itineraries — use CALENDAR_ACTION instead. " +
|
|
1944
|
-
"This action provides the final grounded reply; do not pair it with a speculative REPLY action or fall back to advice-only chat when the user wants real LifeOps follow-through.",
|
|
1945
|
-
suppressPostActionContinuation: true,
|
|
1946
|
-
validate: async (runtime, message) => {
|
|
1947
|
-
return hasLifeOpsAccess(runtime, message);
|
|
1948
|
-
},
|
|
1949
|
-
handler: async (runtime, message, state, options) => {
|
|
1950
|
-
if (!(await hasLifeOpsAccess(runtime, message))) {
|
|
1951
|
-
const fallback = "Life management is restricted to the owner, explicitly granted users, and the agent.";
|
|
1952
|
-
return {
|
|
1953
|
-
success: false,
|
|
1954
|
-
text: await renderLifeActionReply({
|
|
1955
|
-
runtime,
|
|
1956
|
-
message,
|
|
1957
|
-
state,
|
|
1958
|
-
intent: normalizeLifeInputText(messageText(message)),
|
|
1959
|
-
scenario: "reply_only",
|
|
1960
|
-
fallback,
|
|
1961
|
-
context: {
|
|
1962
|
-
reason: "access_restricted",
|
|
1963
|
-
},
|
|
1964
|
-
}),
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
const rawParams = options?.parameters;
|
|
1968
|
-
const params = rawParams ?? {};
|
|
1969
|
-
const currentText = normalizeLifeInputText(messageText(message));
|
|
1970
|
-
const details = params.details;
|
|
1971
|
-
const deferredDraft = latestDeferredLifeDraft(state);
|
|
1972
|
-
const turnsSinceDraft = deferredDraft != null
|
|
1973
|
-
? (countTurnsSinceLatestDeferredLifeDraft(state) ?? 0) + 1
|
|
1974
|
-
: undefined;
|
|
1975
|
-
const draftExpiryReason = deferredLifeDraftExpiryReason({
|
|
1976
|
-
draft: deferredDraft,
|
|
1977
|
-
turnsSinceDraft,
|
|
1978
|
-
});
|
|
1979
|
-
if (draftExpiryReason && looksLikeDeferredLifeConfirmation(currentText)) {
|
|
1980
|
-
const fallback = "That LifeOps draft expired. Please restate it and I'll preview it again.";
|
|
1981
|
-
return {
|
|
1982
|
-
success: false,
|
|
1983
|
-
text: await renderLifeActionReply({
|
|
1984
|
-
runtime,
|
|
1985
|
-
message,
|
|
1986
|
-
state,
|
|
1987
|
-
intent: currentText,
|
|
1988
|
-
scenario: "reply_only",
|
|
1989
|
-
fallback,
|
|
1990
|
-
context: {
|
|
1991
|
-
reason: "draft_expired",
|
|
1992
|
-
},
|
|
1993
|
-
}),
|
|
1994
|
-
};
|
|
1995
|
-
}
|
|
1996
|
-
const deferredDraftFollowupMode = deferredDraft
|
|
1997
|
-
? await extractDeferredLifeDraftFollowupWithLlm({
|
|
1998
|
-
runtime,
|
|
1999
|
-
message,
|
|
2000
|
-
state,
|
|
2001
|
-
currentText,
|
|
2002
|
-
draft: deferredDraft,
|
|
2003
|
-
})
|
|
2004
|
-
: null;
|
|
2005
|
-
if (deferredDraftFollowupMode === "cancel") {
|
|
2006
|
-
const fallback = "Okay, I won't save it yet.";
|
|
2007
|
-
return {
|
|
2008
|
-
success: true,
|
|
2009
|
-
text: await renderLifeActionReply({
|
|
2010
|
-
runtime,
|
|
2011
|
-
message,
|
|
2012
|
-
state,
|
|
2013
|
-
intent: currentText,
|
|
2014
|
-
scenario: "reply_only",
|
|
2015
|
-
fallback,
|
|
2016
|
-
context: {
|
|
2017
|
-
reason: "draft_cancelled",
|
|
2018
|
-
draft: deferredDraft
|
|
2019
|
-
? {
|
|
2020
|
-
operation: deferredDraft.operation,
|
|
2021
|
-
title: deferredDraft.request.title,
|
|
2022
|
-
}
|
|
2023
|
-
: null,
|
|
2024
|
-
},
|
|
2025
|
-
}),
|
|
2026
|
-
data: {
|
|
2027
|
-
actionName: "LIFE",
|
|
2028
|
-
noop: true,
|
|
2029
|
-
},
|
|
2030
|
-
};
|
|
2031
|
-
}
|
|
2032
|
-
const deferredDraftReuseMode = resolveDeferredLifeDraftReuseMode({
|
|
2033
|
-
currentText,
|
|
2034
|
-
details,
|
|
2035
|
-
draft: deferredDraft,
|
|
2036
|
-
explicitAction: params.action,
|
|
2037
|
-
paramsIntent: params.intent,
|
|
2038
|
-
target: params.target,
|
|
2039
|
-
title: params.title,
|
|
2040
|
-
llmMode: deferredDraftFollowupMode,
|
|
2041
|
-
turnsSinceDraft,
|
|
2042
|
-
});
|
|
2043
|
-
const reuseDeferredDraft = deferredDraftReuseMode !== null;
|
|
2044
|
-
const intent = reuseDeferredDraft
|
|
2045
|
-
? deferredDraftReuseMode === "confirm"
|
|
2046
|
-
? normalizeLifeInputText(deferredDraft?.intent ?? "")
|
|
2047
|
-
: normalizeLifeInputText(params.intent?.trim() ?? currentText)
|
|
2048
|
-
: normalizeLifeInputText(params.intent?.trim() ?? currentText);
|
|
2049
|
-
if (!intent) {
|
|
2050
|
-
const fallback = "Tell me what you want me to do.";
|
|
2051
|
-
return {
|
|
2052
|
-
success: false,
|
|
2053
|
-
text: await renderLifeActionReply({
|
|
2054
|
-
runtime,
|
|
2055
|
-
message,
|
|
2056
|
-
state,
|
|
2057
|
-
intent: currentText,
|
|
2058
|
-
scenario: "reply_only",
|
|
2059
|
-
fallback,
|
|
2060
|
-
context: {
|
|
2061
|
-
reason: "missing_intent",
|
|
2062
|
-
},
|
|
2063
|
-
}),
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
const explicitOperation = params.action
|
|
2067
|
-
? ACTION_TO_OPERATION[params.action]
|
|
2068
|
-
: undefined;
|
|
2069
|
-
const operationPlan = reuseDeferredDraft && deferredDraft
|
|
2070
|
-
? {
|
|
2071
|
-
confidence: 1,
|
|
2072
|
-
missing: [],
|
|
2073
|
-
operation: deferredDraft.operation,
|
|
2074
|
-
shouldAct: true,
|
|
2075
|
-
}
|
|
2076
|
-
: await resolveLifeOperationPlan({
|
|
2077
|
-
runtime,
|
|
2078
|
-
message,
|
|
2079
|
-
state,
|
|
2080
|
-
intent,
|
|
2081
|
-
explicitOperation,
|
|
2082
|
-
});
|
|
2083
|
-
const forceCreateExecution = shouldForceLifeCreateExecution({
|
|
2084
|
-
intent,
|
|
2085
|
-
missing: operationPlan.missing,
|
|
2086
|
-
operation: operationPlan.operation,
|
|
2087
|
-
details,
|
|
2088
|
-
title: params.title,
|
|
2089
|
-
});
|
|
2090
|
-
if (!operationPlan.shouldAct && !forceCreateExecution) {
|
|
2091
|
-
const fallback = buildLifeClarificationFallback({
|
|
2092
|
-
missing: operationPlan.missing,
|
|
2093
|
-
operation: operationPlan.operation,
|
|
2094
|
-
});
|
|
2095
|
-
return {
|
|
2096
|
-
success: true,
|
|
2097
|
-
text: await renderLifeActionReply({
|
|
2098
|
-
runtime,
|
|
2099
|
-
message,
|
|
2100
|
-
state,
|
|
2101
|
-
intent,
|
|
2102
|
-
scenario: operationPlan.operation === "create_goal"
|
|
2103
|
-
? "clarify_create_goal"
|
|
2104
|
-
: "clarify_create_definition",
|
|
2105
|
-
fallback,
|
|
2106
|
-
context: {
|
|
2107
|
-
missing: operationPlan.missing,
|
|
2108
|
-
operation: operationPlan.operation,
|
|
2109
|
-
},
|
|
2110
|
-
}),
|
|
2111
|
-
data: {
|
|
2112
|
-
actionName: "LIFE",
|
|
2113
|
-
noop: true,
|
|
2114
|
-
suggestedOperation: operationPlan.operation,
|
|
2115
|
-
},
|
|
2116
|
-
};
|
|
2117
|
-
}
|
|
2118
|
-
const operation = (forceCreateExecution ? "create_definition" : operationPlan.operation) ??
|
|
2119
|
-
classifyIntent(intent);
|
|
2120
|
-
const service = new LifeOpsService(runtime);
|
|
2121
|
-
const domain = detailString(details, "domain");
|
|
2122
|
-
const ownership = requestedOwnership(domain);
|
|
2123
|
-
const chatText = intent;
|
|
2124
|
-
const targetName = params.target ?? params.title;
|
|
2125
|
-
const createConfirmed = deferredDraftReuseMode === "confirm" ||
|
|
2126
|
-
detailBoolean(details, "confirmed") === true;
|
|
2127
|
-
try {
|
|
2128
|
-
const createDefinition = async () => {
|
|
2129
|
-
const deferredDefinitionDraft = reuseDeferredDraft && deferredDraft?.operation === "create_definition"
|
|
2130
|
-
? deferredDraft
|
|
2131
|
-
: null;
|
|
2132
|
-
const editingDeferredDefinitionDraft = deferredDraftReuseMode === "edit" &&
|
|
2133
|
-
deferredDefinitionDraft?.operation === "create_definition";
|
|
2134
|
-
const explicitCadenceDetail = normalizeCadenceDetail(detailObject(details, "cadence"));
|
|
2135
|
-
const fallbackTitle = deferredDefinitionDraft?.request.title ?? null;
|
|
2136
|
-
let title = editingDeferredDefinitionDraft
|
|
2137
|
-
? (params.title ?? fallbackTitle)
|
|
2138
|
-
: (fallbackTitle ?? params.title ?? null);
|
|
2139
|
-
const fallbackCadence = deferredDefinitionDraft?.request.cadence;
|
|
2140
|
-
let cadence = editingDeferredDefinitionDraft
|
|
2141
|
-
? (explicitCadenceDetail ?? fallbackCadence ?? undefined)
|
|
2142
|
-
: (fallbackCadence ?? explicitCadenceDetail ?? undefined);
|
|
2143
|
-
let windowPolicy = editingDeferredDefinitionDraft
|
|
2144
|
-
? (detailObject(details, "windowPolicy") ?? deferredDefinitionDraft?.request.windowPolicy)
|
|
2145
|
-
: (deferredDefinitionDraft?.request.windowPolicy ??
|
|
2146
|
-
detailObject(details, "windowPolicy"));
|
|
2147
|
-
const explicitPriority = detailNumber(details, "priority");
|
|
2148
|
-
const explicitDescription = detailString(details, "description");
|
|
2149
|
-
const explicitMetadata = detailObject(details, "metadata");
|
|
2150
|
-
// Track whether cadence/title came from explicit high-confidence
|
|
2151
|
-
// sources so the planner only fills genuine gaps.
|
|
2152
|
-
const hadExplicitCadence = Boolean((editingDeferredDefinitionDraft
|
|
2153
|
-
? (explicitCadenceDetail ??
|
|
2154
|
-
deferredDefinitionDraft?.request.cadence)
|
|
2155
|
-
: deferredDefinitionDraft?.request.cadence) ??
|
|
2156
|
-
explicitCadenceDetail);
|
|
2157
|
-
const hadExplicitTitle = Boolean((editingDeferredDefinitionDraft
|
|
2158
|
-
? params.title
|
|
2159
|
-
: deferredDefinitionDraft?.request.title) ?? params.title);
|
|
2160
|
-
// ── LLM parameter enhancement (fills gaps) ────────
|
|
2161
|
-
// Skip when reusing a confirmed deferred draft — the user already
|
|
2162
|
-
// approved those values.
|
|
2163
|
-
let llmPlan = null;
|
|
2164
|
-
let llmDescription;
|
|
2165
|
-
let llmPriority;
|
|
2166
|
-
let llmRequestKind = null;
|
|
2167
|
-
if (!deferredDefinitionDraft || editingDeferredDefinitionDraft) {
|
|
2168
|
-
llmPlan = await extractTaskCreatePlanWithLlm({
|
|
2169
|
-
runtime,
|
|
2170
|
-
intent,
|
|
2171
|
-
state: state ?? undefined,
|
|
2172
|
-
message: message ?? undefined,
|
|
2173
|
-
});
|
|
2174
|
-
const shouldHonorPlannerResponse = llmPlan?.mode === "respond" &&
|
|
2175
|
-
Boolean(llmPlan.response) &&
|
|
2176
|
-
!editingDeferredDefinitionDraft &&
|
|
2177
|
-
!params.title &&
|
|
2178
|
-
!explicitCadenceDetail &&
|
|
2179
|
-
!detailString(details, "description") &&
|
|
2180
|
-
!detailString(details, "goalId") &&
|
|
2181
|
-
!detailString(details, "goalTitle") &&
|
|
2182
|
-
!detailString(details, "kind");
|
|
2183
|
-
if (shouldHonorPlannerResponse && llmPlan?.response) {
|
|
2184
|
-
return {
|
|
2185
|
-
success: true,
|
|
2186
|
-
text: llmPlan.response,
|
|
2187
|
-
};
|
|
2188
|
-
}
|
|
2189
|
-
if (llmPlan) {
|
|
2190
|
-
llmRequestKind = llmPlan.requestKind;
|
|
2191
|
-
if (!hadExplicitTitle &&
|
|
2192
|
-
shouldAdoptPlannerTitle({
|
|
2193
|
-
currentTitle: title,
|
|
2194
|
-
plannerTitle: llmPlan.title,
|
|
2195
|
-
})) {
|
|
2196
|
-
title = llmPlan.title;
|
|
2197
|
-
}
|
|
2198
|
-
if ((editingDeferredDefinitionDraft || !hadExplicitCadence) &&
|
|
2199
|
-
llmPlan.cadenceKind) {
|
|
2200
|
-
const llmCadenceTimeZone = normalizeLifeTimeZoneToken(detailString(details, "timeZone") ??
|
|
2201
|
-
llmPlan.timeZone ??
|
|
2202
|
-
deferredDefinitionDraft?.request.timezone ??
|
|
2203
|
-
windowPolicy?.timezone) ?? extractLifeTimeZoneFromText(intent);
|
|
2204
|
-
const llmCadence = buildCadenceFromLlmParams(llmPlan, {
|
|
2205
|
-
intent,
|
|
2206
|
-
timeZone: llmCadenceTimeZone ?? undefined,
|
|
2207
|
-
});
|
|
2208
|
-
if (llmCadence &&
|
|
2209
|
-
shouldAdoptPlannerCadence({
|
|
2210
|
-
currentCadence: cadence,
|
|
2211
|
-
plannerCadence: llmCadence.cadence,
|
|
2212
|
-
})) {
|
|
2213
|
-
cadence = llmCadence.cadence;
|
|
2214
|
-
windowPolicy = llmCadence.windowPolicy ?? windowPolicy;
|
|
2215
|
-
}
|
|
2216
|
-
}
|
|
2217
|
-
if (!explicitDescription && llmPlan.description) {
|
|
2218
|
-
llmDescription = llmPlan.description;
|
|
2219
|
-
}
|
|
2220
|
-
if (explicitPriority === undefined && llmPlan.priority) {
|
|
2221
|
-
llmPriority = llmPlan.priority;
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
const resolvedTimeZone = normalizeLifeTimeZoneToken(detailString(details, "timeZone") ??
|
|
2226
|
-
llmPlan?.timeZone ??
|
|
2227
|
-
deferredDefinitionDraft?.request.timezone ??
|
|
2228
|
-
windowPolicy?.timezone) ?? extractLifeTimeZoneFromText(intent);
|
|
2229
|
-
const timedRequestKind = llmRequestKind;
|
|
2230
|
-
const nativeAppleMetadata = timedRequestKind && cadence?.kind === "once"
|
|
2231
|
-
? buildNativeAppleReminderMetadata({
|
|
2232
|
-
kind: timedRequestKind,
|
|
2233
|
-
source: "llm",
|
|
2234
|
-
})
|
|
2235
|
-
: undefined;
|
|
2236
|
-
const definitionMetadata = editingDeferredDefinitionDraft
|
|
2237
|
-
? mergeMetadataRecords(deferredDefinitionDraft?.request.metadata, mergeMetadataRecords(explicitMetadata, nativeAppleMetadata))
|
|
2238
|
-
: (deferredDefinitionDraft?.request.metadata ??
|
|
2239
|
-
mergeMetadataRecords(explicitMetadata, nativeAppleMetadata));
|
|
2240
|
-
if (!title) {
|
|
2241
|
-
const fallback = "What should I call it?";
|
|
2242
|
-
return {
|
|
2243
|
-
success: false,
|
|
2244
|
-
text: await renderLifeActionReply({
|
|
2245
|
-
runtime,
|
|
2246
|
-
message,
|
|
2247
|
-
state,
|
|
2248
|
-
intent,
|
|
2249
|
-
scenario: "clarify_create_definition",
|
|
2250
|
-
fallback,
|
|
2251
|
-
context: {
|
|
2252
|
-
missing: ["title"],
|
|
2253
|
-
operation: "create_definition",
|
|
2254
|
-
},
|
|
2255
|
-
}),
|
|
2256
|
-
};
|
|
2257
|
-
}
|
|
2258
|
-
if (!cadence) {
|
|
2259
|
-
const fallback = "When should it happen?";
|
|
2260
|
-
return {
|
|
2261
|
-
success: false,
|
|
2262
|
-
text: await renderLifeActionReply({
|
|
2263
|
-
runtime,
|
|
2264
|
-
message,
|
|
2265
|
-
state,
|
|
2266
|
-
intent,
|
|
2267
|
-
scenario: "clarify_create_definition",
|
|
2268
|
-
fallback,
|
|
2269
|
-
context: {
|
|
2270
|
-
missing: ["schedule"],
|
|
2271
|
-
operation: "create_definition",
|
|
2272
|
-
},
|
|
2273
|
-
}),
|
|
2274
|
-
};
|
|
2275
|
-
}
|
|
2276
|
-
const kind = (editingDeferredDefinitionDraft
|
|
2277
|
-
? detailString(details, "kind")
|
|
2278
|
-
: deferredDefinitionDraft?.request.kind) ??
|
|
2279
|
-
detailString(details, "kind") ??
|
|
2280
|
-
"habit";
|
|
2281
|
-
const definitionDraft = {
|
|
2282
|
-
intent,
|
|
2283
|
-
operation: "create_definition",
|
|
2284
|
-
createdAt: editingDeferredDefinitionDraft
|
|
2285
|
-
? Date.now()
|
|
2286
|
-
: (deferredDefinitionDraft?.createdAt ?? Date.now()),
|
|
2287
|
-
request: {
|
|
2288
|
-
cadence,
|
|
2289
|
-
description: explicitDescription ??
|
|
2290
|
-
llmDescription ??
|
|
2291
|
-
(editingDeferredDefinitionDraft
|
|
2292
|
-
? deferredDefinitionDraft?.request.description
|
|
2293
|
-
: undefined),
|
|
2294
|
-
goalRef: detailString(details, "goalId") ??
|
|
2295
|
-
detailString(details, "goalTitle") ??
|
|
2296
|
-
deferredDefinitionDraft?.request.goalRef ??
|
|
2297
|
-
undefined,
|
|
2298
|
-
kind,
|
|
2299
|
-
priority: explicitPriority ??
|
|
2300
|
-
llmPriority ??
|
|
2301
|
-
deferredDefinitionDraft?.request.priority,
|
|
2302
|
-
progressionRule: detailObject(details, "progressionRule") ??
|
|
2303
|
-
deferredDefinitionDraft?.request.progressionRule,
|
|
2304
|
-
reminderPlan: detailObject(details, "reminderPlan") ??
|
|
2305
|
-
deferredDefinitionDraft?.request.reminderPlan ??
|
|
2306
|
-
buildDefaultReminderPlan(`${title} reminder`),
|
|
2307
|
-
timezone: extractLifeTimeZoneFromText(intent) ??
|
|
2308
|
-
normalizeLifeTimeZoneToken(llmPlan?.timeZone) ??
|
|
2309
|
-
normalizeLifeTimeZoneToken(resolvedTimeZone ?? deferredDefinitionDraft?.request.timezone) ??
|
|
2310
|
-
resolvedTimeZone ??
|
|
2311
|
-
deferredDefinitionDraft?.request.timezone,
|
|
2312
|
-
title,
|
|
2313
|
-
metadata: definitionMetadata,
|
|
2314
|
-
windowPolicy,
|
|
2315
|
-
websiteAccess: detailObject(details, "websiteAccess") ?? deferredDefinitionDraft?.request.websiteAccess,
|
|
2316
|
-
},
|
|
2317
|
-
};
|
|
2318
|
-
if (shouldRequireLifeCreateConfirmation({
|
|
2319
|
-
confirmed: createConfirmed,
|
|
2320
|
-
messageSource: typeof message.content?.source === "string"
|
|
2321
|
-
? message.content.source
|
|
2322
|
-
: undefined,
|
|
2323
|
-
requestKind: timedRequestKind,
|
|
2324
|
-
cadence: definitionDraft.request.cadence,
|
|
2325
|
-
})) {
|
|
2326
|
-
const fallback = `I can save this as a ${definitionDraft.request.kind} named "${definitionDraft.request.title}" that happens ${summarizeCadence(definitionDraft.request.cadence)}. Confirm and I'll save it, or tell me what to change.`;
|
|
2327
|
-
return {
|
|
2328
|
-
success: true,
|
|
2329
|
-
text: await renderLifeActionReply({
|
|
2330
|
-
runtime,
|
|
2331
|
-
message,
|
|
2332
|
-
state,
|
|
2333
|
-
intent,
|
|
2334
|
-
scenario: "preview_definition",
|
|
2335
|
-
fallback,
|
|
2336
|
-
context: {
|
|
2337
|
-
draft: definitionDraft.request,
|
|
2338
|
-
requestKind: timedRequestKind,
|
|
2339
|
-
},
|
|
2340
|
-
}),
|
|
2341
|
-
data: {
|
|
2342
|
-
actionName: "LIFE",
|
|
2343
|
-
deferred: true,
|
|
2344
|
-
lifeDraft: definitionDraft,
|
|
2345
|
-
preview: {
|
|
2346
|
-
cadence: definitionDraft.request.cadence,
|
|
2347
|
-
kind: definitionDraft.request.kind,
|
|
2348
|
-
title: definitionDraft.request.title,
|
|
2349
|
-
},
|
|
2350
|
-
},
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
const resolvedGoal = definitionDraft.request.goalRef
|
|
2354
|
-
? await resolveGoal(service, definitionDraft.request.goalRef, domain)
|
|
2355
|
-
: null;
|
|
2356
|
-
const created = await service.createDefinition({
|
|
2357
|
-
ownership,
|
|
2358
|
-
kind: definitionDraft.request.kind,
|
|
2359
|
-
title: definitionDraft.request.title,
|
|
2360
|
-
description: definitionDraft.request.description,
|
|
2361
|
-
originalIntent: definitionDraft.intent || definitionDraft.request.title,
|
|
2362
|
-
cadence: definitionDraft.request.cadence,
|
|
2363
|
-
timezone: extractLifeTimeZoneFromText(definitionDraft.intent) ??
|
|
2364
|
-
normalizeLifeTimeZoneToken(definitionDraft.request.timezone) ??
|
|
2365
|
-
definitionDraft.request.timezone,
|
|
2366
|
-
priority: definitionDraft.request.priority,
|
|
2367
|
-
windowPolicy: definitionDraft.request.windowPolicy,
|
|
2368
|
-
progressionRule: definitionDraft.request.progressionRule,
|
|
2369
|
-
reminderPlan: definitionDraft.request.reminderPlan,
|
|
2370
|
-
metadata: definitionDraft.request.metadata,
|
|
2371
|
-
websiteAccess: definitionDraft.request.websiteAccess,
|
|
2372
|
-
goalId: resolvedGoal?.goal.id ?? null,
|
|
2373
|
-
source: "chat",
|
|
2374
|
-
});
|
|
2375
|
-
const fallback = `Saved "${created.definition.title}" as ${summarizeCadence(created.definition.cadence)}.`;
|
|
2376
|
-
return {
|
|
2377
|
-
success: true,
|
|
2378
|
-
text: await renderLifeActionReply({
|
|
2379
|
-
runtime,
|
|
2380
|
-
message,
|
|
2381
|
-
state,
|
|
2382
|
-
intent,
|
|
2383
|
-
scenario: "saved_definition",
|
|
2384
|
-
fallback,
|
|
2385
|
-
context: {
|
|
2386
|
-
created: {
|
|
2387
|
-
title: created.definition.title,
|
|
2388
|
-
cadence: created.definition.cadence,
|
|
2389
|
-
},
|
|
2390
|
-
requestKind: timedRequestKind,
|
|
2391
|
-
},
|
|
2392
|
-
}),
|
|
2393
|
-
data: toActionData(created),
|
|
2394
|
-
};
|
|
2395
|
-
};
|
|
2396
|
-
// ── Queries ─────────────────────────────────────
|
|
2397
|
-
if (operation === "query_calendar_today" ||
|
|
2398
|
-
operation === "query_calendar_next") {
|
|
2399
|
-
const google = await getGoogleCapabilityStatus(service);
|
|
2400
|
-
if (!google.hasCalendarRead) {
|
|
2401
|
-
return {
|
|
2402
|
-
success: false,
|
|
2403
|
-
text: calendarReadUnavailableMessage(google),
|
|
2404
|
-
};
|
|
2405
|
-
}
|
|
2406
|
-
if (operation === "query_calendar_next") {
|
|
2407
|
-
const ctx = await service.getNextCalendarEventContext(INTERNAL_URL);
|
|
2408
|
-
return {
|
|
2409
|
-
success: true,
|
|
2410
|
-
text: formatNextEventContext(ctx),
|
|
2411
|
-
data: toActionData(ctx),
|
|
2412
|
-
};
|
|
2413
|
-
}
|
|
2414
|
-
const timeRangeHint = intent.toLowerCase();
|
|
2415
|
-
const range = /\btomorrow\b/.test(timeRangeHint)
|
|
2416
|
-
? dayRange(1)
|
|
2417
|
-
: /\b(this week|week)\b/.test(timeRangeHint)
|
|
2418
|
-
? weekRange()
|
|
2419
|
-
: dayRange(0);
|
|
2420
|
-
const label = /\btomorrow\b/.test(timeRangeHint)
|
|
2421
|
-
? "tomorrow"
|
|
2422
|
-
: /\b(this week|week)\b/.test(timeRangeHint)
|
|
2423
|
-
? "this week"
|
|
2424
|
-
: "today";
|
|
2425
|
-
const feed = await service.getCalendarFeed(INTERNAL_URL, {
|
|
2426
|
-
timeMin: range.timeMin,
|
|
2427
|
-
timeMax: range.timeMax,
|
|
2428
|
-
});
|
|
2429
|
-
return {
|
|
2430
|
-
success: true,
|
|
2431
|
-
text: formatCalendarFeed(feed, label),
|
|
2432
|
-
data: toActionData(feed),
|
|
2433
|
-
};
|
|
2434
|
-
}
|
|
2435
|
-
if (operation === "query_email") {
|
|
2436
|
-
const limit = detailNumber(details, "limit") ?? 10;
|
|
2437
|
-
return ((await gmailAction.handler?.(runtime, message, state, {
|
|
2438
|
-
parameters: {
|
|
2439
|
-
subaction: "triage",
|
|
2440
|
-
intent,
|
|
2441
|
-
details: {
|
|
2442
|
-
...details,
|
|
2443
|
-
maxResults: limit,
|
|
2444
|
-
},
|
|
2445
|
-
},
|
|
2446
|
-
})) ?? {
|
|
2447
|
-
success: false,
|
|
2448
|
-
text: "I couldn't route that Gmail request yet.",
|
|
2449
|
-
});
|
|
2450
|
-
}
|
|
2451
|
-
if (operation === "query_overview") {
|
|
2452
|
-
const overview = await service.getOverview();
|
|
2453
|
-
const userQuery = messageText(message) || intent || "overview";
|
|
2454
|
-
const fallback = formatOverviewForQuery(overview, userQuery);
|
|
2455
|
-
return {
|
|
2456
|
-
success: true,
|
|
2457
|
-
text: await renderLifeActionReply({
|
|
2458
|
-
runtime,
|
|
2459
|
-
message,
|
|
2460
|
-
state,
|
|
2461
|
-
intent: userQuery,
|
|
2462
|
-
scenario: "overview",
|
|
2463
|
-
fallback,
|
|
2464
|
-
context: {
|
|
2465
|
-
summary: overview.owner.summary,
|
|
2466
|
-
occurrenceTitles: overview.owner.occurrences
|
|
2467
|
-
.slice(0, 6)
|
|
2468
|
-
.map((occurrence) => occurrence.title),
|
|
2469
|
-
goalTitles: overview.owner.goals
|
|
2470
|
-
.slice(0, 3)
|
|
2471
|
-
.map((goal) => goal.title),
|
|
2472
|
-
},
|
|
2473
|
-
}),
|
|
2474
|
-
data: toActionData(overview),
|
|
2475
|
-
};
|
|
2476
|
-
}
|
|
2477
|
-
// ── Mutations ───────────────────────────────────
|
|
2478
|
-
if (operation === "create_definition") {
|
|
2479
|
-
return await createDefinition();
|
|
2480
|
-
}
|
|
2481
|
-
if (operation === "create_goal") {
|
|
2482
|
-
const deferredGoalDraft = reuseDeferredDraft && deferredDraft?.operation === "create_goal"
|
|
2483
|
-
? deferredDraft
|
|
2484
|
-
: null;
|
|
2485
|
-
const editingDeferredGoalDraft = deferredDraftReuseMode === "edit" &&
|
|
2486
|
-
deferredGoalDraft?.operation === "create_goal";
|
|
2487
|
-
const explicitDescription = detailString(details, "description");
|
|
2488
|
-
const explicitCadence = normalizeCadenceDetail(detailObject(details, "cadence"));
|
|
2489
|
-
const explicitSuccessCriteria = detailObject(details, "successCriteria");
|
|
2490
|
-
const explicitSupportStrategy = detailObject(details, "supportStrategy");
|
|
2491
|
-
const explicitMetadata = detailObject(details, "metadata");
|
|
2492
|
-
let title = editingDeferredGoalDraft
|
|
2493
|
-
? (params.title ?? deferredGoalDraft?.request.title ?? null)
|
|
2494
|
-
: (deferredGoalDraft?.request.title ?? params.title ?? null);
|
|
2495
|
-
let description = editingDeferredGoalDraft
|
|
2496
|
-
? (explicitDescription ?? deferredGoalDraft?.request.description)
|
|
2497
|
-
: (deferredGoalDraft?.request.description ?? explicitDescription);
|
|
2498
|
-
let cadence = editingDeferredGoalDraft
|
|
2499
|
-
? (explicitCadence ?? deferredGoalDraft?.request.cadence)
|
|
2500
|
-
: (deferredGoalDraft?.request.cadence ?? explicitCadence);
|
|
2501
|
-
let successCriteria = editingDeferredGoalDraft
|
|
2502
|
-
? (explicitSuccessCriteria ??
|
|
2503
|
-
deferredGoalDraft?.request.successCriteria)
|
|
2504
|
-
: (deferredGoalDraft?.request.successCriteria ??
|
|
2505
|
-
explicitSuccessCriteria);
|
|
2506
|
-
let supportStrategy = editingDeferredGoalDraft
|
|
2507
|
-
? (explicitSupportStrategy ??
|
|
2508
|
-
deferredGoalDraft?.request.supportStrategy)
|
|
2509
|
-
: (deferredGoalDraft?.request.supportStrategy ??
|
|
2510
|
-
explicitSupportStrategy);
|
|
2511
|
-
let goalMetadata = editingDeferredGoalDraft
|
|
2512
|
-
? (explicitMetadata ?? deferredGoalDraft?.request.metadata)
|
|
2513
|
-
: (deferredGoalDraft?.request.metadata ?? explicitMetadata);
|
|
2514
|
-
let evaluationSummary = null;
|
|
2515
|
-
if (!deferredGoalDraft || editingDeferredGoalDraft) {
|
|
2516
|
-
const llmPlan = await extractGoalCreatePlanWithLlm({
|
|
2517
|
-
runtime,
|
|
2518
|
-
intent,
|
|
2519
|
-
state: state ?? undefined,
|
|
2520
|
-
message: message ?? undefined,
|
|
2521
|
-
});
|
|
2522
|
-
if (!title && llmPlan.title) {
|
|
2523
|
-
title = llmPlan.title;
|
|
2524
|
-
}
|
|
2525
|
-
if (!description && llmPlan.description) {
|
|
2526
|
-
description = llmPlan.description;
|
|
2527
|
-
}
|
|
2528
|
-
if (!cadence && llmPlan.cadence) {
|
|
2529
|
-
cadence = llmPlan.cadence;
|
|
2530
|
-
}
|
|
2531
|
-
if (!successCriteria && llmPlan.successCriteria) {
|
|
2532
|
-
successCriteria = llmPlan.successCriteria;
|
|
2533
|
-
}
|
|
2534
|
-
if (!supportStrategy && llmPlan.supportStrategy) {
|
|
2535
|
-
supportStrategy = llmPlan.supportStrategy;
|
|
2536
|
-
}
|
|
2537
|
-
evaluationSummary = llmPlan.evaluationSummary;
|
|
2538
|
-
if (llmPlan.groundingState === "grounded" &&
|
|
2539
|
-
llmPlan.successCriteria &&
|
|
2540
|
-
title) {
|
|
2541
|
-
goalMetadata = mergeGoalMetadataWithGrounding({
|
|
2542
|
-
metadata: {
|
|
2543
|
-
...(goalMetadata ?? {}),
|
|
2544
|
-
source: "chat",
|
|
2545
|
-
originalIntent: intent,
|
|
2546
|
-
},
|
|
2547
|
-
nowIso: new Date().toISOString(),
|
|
2548
|
-
plan: llmPlan,
|
|
2549
|
-
});
|
|
2550
|
-
}
|
|
2551
|
-
if (llmPlan.groundingState !== "grounded" ||
|
|
2552
|
-
!title ||
|
|
2553
|
-
!successCriteria ||
|
|
2554
|
-
!supportStrategy) {
|
|
2555
|
-
return {
|
|
2556
|
-
success: true,
|
|
2557
|
-
text: llmPlan.response ??
|
|
2558
|
-
"What would count as success for that goal, and over what time window?",
|
|
2559
|
-
data: {
|
|
2560
|
-
actionName: "LIFE",
|
|
2561
|
-
noop: true,
|
|
2562
|
-
suggestedOperation: "create_goal",
|
|
2563
|
-
},
|
|
2564
|
-
};
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
|
-
if (!title)
|
|
2568
|
-
return {
|
|
2569
|
-
success: false,
|
|
2570
|
-
text: await renderLifeActionReply({
|
|
2571
|
-
runtime,
|
|
2572
|
-
message,
|
|
2573
|
-
state,
|
|
2574
|
-
intent,
|
|
2575
|
-
scenario: "clarify_create_goal",
|
|
2576
|
-
fallback: "What are you trying to achieve?",
|
|
2577
|
-
context: {
|
|
2578
|
-
missing: ["title"],
|
|
2579
|
-
operation: "create_goal",
|
|
2580
|
-
},
|
|
2581
|
-
}),
|
|
2582
|
-
};
|
|
2583
|
-
const goalDraft = deferredGoalDraft ?? {
|
|
2584
|
-
intent,
|
|
2585
|
-
operation: "create_goal",
|
|
2586
|
-
createdAt: Date.now(),
|
|
2587
|
-
request: {
|
|
2588
|
-
cadence,
|
|
2589
|
-
description,
|
|
2590
|
-
metadata: goalMetadata,
|
|
2591
|
-
successCriteria,
|
|
2592
|
-
supportStrategy,
|
|
2593
|
-
title,
|
|
2594
|
-
},
|
|
2595
|
-
};
|
|
2596
|
-
if (shouldRequireLifeCreateConfirmation({
|
|
2597
|
-
confirmed: createConfirmed,
|
|
2598
|
-
messageSource: typeof message.content?.source === "string"
|
|
2599
|
-
? message.content.source
|
|
2600
|
-
: undefined,
|
|
2601
|
-
})) {
|
|
2602
|
-
const fallback = evaluationSummary
|
|
2603
|
-
? `I can save "${goalDraft.request.title}" as a goal. Success looks like this: ${evaluationSummary} Confirm and I'll save it, or tell me what to change.`
|
|
2604
|
-
: `I can save this goal as "${goalDraft.request.title}". Confirm and I'll save it, or tell me what to change.`;
|
|
2605
|
-
return {
|
|
2606
|
-
success: true,
|
|
2607
|
-
text: await renderLifeActionReply({
|
|
2608
|
-
runtime,
|
|
2609
|
-
message,
|
|
2610
|
-
state,
|
|
2611
|
-
intent,
|
|
2612
|
-
scenario: "preview_goal",
|
|
2613
|
-
fallback,
|
|
2614
|
-
context: {
|
|
2615
|
-
draft: goalDraft.request,
|
|
2616
|
-
groundingSummary: evaluationSummary,
|
|
2617
|
-
},
|
|
2618
|
-
}),
|
|
2619
|
-
data: {
|
|
2620
|
-
actionName: "LIFE",
|
|
2621
|
-
deferred: true,
|
|
2622
|
-
lifeDraft: goalDraft,
|
|
2623
|
-
preview: {
|
|
2624
|
-
title: goalDraft.request.title,
|
|
2625
|
-
},
|
|
2626
|
-
},
|
|
2627
|
-
};
|
|
2628
|
-
}
|
|
2629
|
-
const created = await service.createGoal({
|
|
2630
|
-
ownership,
|
|
2631
|
-
title: goalDraft.request.title,
|
|
2632
|
-
description: goalDraft.request.description,
|
|
2633
|
-
cadence: goalDraft.request.cadence,
|
|
2634
|
-
supportStrategy: goalDraft.request.supportStrategy,
|
|
2635
|
-
successCriteria: goalDraft.request.successCriteria,
|
|
2636
|
-
metadata: {
|
|
2637
|
-
...(goalDraft.request.metadata ?? {}),
|
|
2638
|
-
source: "chat",
|
|
2639
|
-
originalIntent: goalDraft.intent || goalDraft.request.title,
|
|
2640
|
-
},
|
|
2641
|
-
});
|
|
2642
|
-
const fallback = `Saved goal "${created.goal.title}".`;
|
|
2643
|
-
return {
|
|
2644
|
-
success: true,
|
|
2645
|
-
text: await renderLifeActionReply({
|
|
2646
|
-
runtime,
|
|
2647
|
-
message,
|
|
2648
|
-
state,
|
|
2649
|
-
intent,
|
|
2650
|
-
scenario: "saved_goal",
|
|
2651
|
-
fallback,
|
|
2652
|
-
context: {
|
|
2653
|
-
created: {
|
|
2654
|
-
title: created.goal.title,
|
|
2655
|
-
cadence: created.goal.cadence,
|
|
2656
|
-
},
|
|
2657
|
-
},
|
|
2658
|
-
}),
|
|
2659
|
-
data: toActionData(created),
|
|
2660
|
-
};
|
|
2661
|
-
}
|
|
2662
|
-
if (operation === "update_definition") {
|
|
2663
|
-
const target = await resolveDefinition(service, targetName, domain);
|
|
2664
|
-
if (!target)
|
|
2665
|
-
return {
|
|
2666
|
-
success: false,
|
|
2667
|
-
text: "I could not find that item to update.",
|
|
2668
|
-
};
|
|
2669
|
-
const request = {
|
|
2670
|
-
ownership,
|
|
2671
|
-
title: params.title !== target.definition.title ? params.title : undefined,
|
|
2672
|
-
description: detailString(details, "description"),
|
|
2673
|
-
cadence: normalizeCadenceDetail(detailObject(details, "cadence")),
|
|
2674
|
-
priority: detailNumber(details, "priority"),
|
|
2675
|
-
windowPolicy: detailObject(details, "windowPolicy"),
|
|
2676
|
-
reminderPlan: detailObject(details, "reminderPlan"),
|
|
2677
|
-
};
|
|
2678
|
-
// If no explicit changes from structured details, try LLM extraction
|
|
2679
|
-
const hasExplicitChanges = hasDefinitionUpdateChanges(request);
|
|
2680
|
-
if (!hasExplicitChanges && intent) {
|
|
2681
|
-
const llmFields = await extractUpdateFieldsWithLlm({
|
|
2682
|
-
runtime,
|
|
2683
|
-
intent,
|
|
2684
|
-
currentTitle: target.definition.title,
|
|
2685
|
-
currentCadenceKind: target.definition.cadence.kind,
|
|
2686
|
-
currentWindows: target.definition.windowPolicy?.windows?.map((w) => w.name) ?? [],
|
|
2687
|
-
});
|
|
2688
|
-
if (llmFields) {
|
|
2689
|
-
if (llmFields.title)
|
|
2690
|
-
request.title = llmFields.title;
|
|
2691
|
-
if (llmFields.priority)
|
|
2692
|
-
request.priority = llmFields.priority;
|
|
2693
|
-
if (llmFields.description)
|
|
2694
|
-
request.description = llmFields.description;
|
|
2695
|
-
if (llmFields.cadenceKind ||
|
|
2696
|
-
llmFields.windows ||
|
|
2697
|
-
llmFields.weekdays ||
|
|
2698
|
-
llmFields.everyMinutes ||
|
|
2699
|
-
llmFields.timeOfDay) {
|
|
2700
|
-
const built = buildCadenceFromUpdateFields({
|
|
2701
|
-
currentCadence: target.definition.cadence,
|
|
2702
|
-
currentWindowPolicy: target.definition.windowPolicy,
|
|
2703
|
-
timeZone: target.definition.timezone,
|
|
2704
|
-
update: llmFields,
|
|
2705
|
-
});
|
|
2706
|
-
if (built) {
|
|
2707
|
-
request.cadence = built.cadence;
|
|
2708
|
-
request.windowPolicy = built.windowPolicy;
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
}
|
|
2712
|
-
}
|
|
2713
|
-
if (!hasDefinitionUpdateChanges(request)) {
|
|
2714
|
-
return {
|
|
2715
|
-
success: false,
|
|
2716
|
-
text: `Tell me what to change about "${target.definition.title}" and I'll update it.`,
|
|
2717
|
-
};
|
|
2718
|
-
}
|
|
2719
|
-
const updated = await service.updateDefinition(target.definition.id, request);
|
|
2720
|
-
const fallback = `Updated "${updated.definition.title}".`;
|
|
2721
|
-
return {
|
|
2722
|
-
success: true,
|
|
2723
|
-
text: await renderLifeActionReply({
|
|
2724
|
-
runtime,
|
|
2725
|
-
message,
|
|
2726
|
-
state,
|
|
2727
|
-
intent,
|
|
2728
|
-
scenario: "updated_definition",
|
|
2729
|
-
fallback,
|
|
2730
|
-
context: {
|
|
2731
|
-
previousTitle: target.definition.title,
|
|
2732
|
-
updated: {
|
|
2733
|
-
title: updated.definition.title,
|
|
2734
|
-
},
|
|
2735
|
-
},
|
|
2736
|
-
}),
|
|
2737
|
-
data: toActionData(updated),
|
|
2738
|
-
};
|
|
2739
|
-
}
|
|
2740
|
-
if (operation === "update_goal") {
|
|
2741
|
-
const target = await resolveGoal(service, targetName, domain);
|
|
2742
|
-
if (!target)
|
|
2743
|
-
return {
|
|
2744
|
-
success: false,
|
|
2745
|
-
text: "I could not find that goal to update.",
|
|
2746
|
-
};
|
|
2747
|
-
const request = {
|
|
2748
|
-
ownership,
|
|
2749
|
-
title: params.title !== target.goal.title ? params.title : undefined,
|
|
2750
|
-
description: detailString(details, "description"),
|
|
2751
|
-
cadence: normalizeCadenceDetail(detailObject(details, "cadence")),
|
|
2752
|
-
supportStrategy: detailObject(details, "supportStrategy"),
|
|
2753
|
-
successCriteria: detailObject(details, "successCriteria"),
|
|
2754
|
-
};
|
|
2755
|
-
const hasExplicitGoalChanges = request.title !== undefined ||
|
|
2756
|
-
request.description !== undefined ||
|
|
2757
|
-
request.cadence !== undefined ||
|
|
2758
|
-
request.supportStrategy !== undefined ||
|
|
2759
|
-
request.successCriteria !== undefined;
|
|
2760
|
-
if (!hasExplicitGoalChanges) {
|
|
2761
|
-
const llmPlan = await extractGoalUpdatePlanWithLlm({
|
|
2762
|
-
runtime,
|
|
2763
|
-
currentGoal: target.goal,
|
|
2764
|
-
intent,
|
|
2765
|
-
state: state ?? undefined,
|
|
2766
|
-
message: message ?? undefined,
|
|
2767
|
-
});
|
|
2768
|
-
if (llmPlan.mode === "respond") {
|
|
2769
|
-
return {
|
|
2770
|
-
success: true,
|
|
2771
|
-
text: llmPlan.response ??
|
|
2772
|
-
`Tell me what to change about "${target.goal.title}" and I'll update it.`,
|
|
2773
|
-
data: {
|
|
2774
|
-
actionName: "LIFE",
|
|
2775
|
-
noop: true,
|
|
2776
|
-
suggestedOperation: "update_goal",
|
|
2777
|
-
},
|
|
2778
|
-
};
|
|
2779
|
-
}
|
|
2780
|
-
if (llmPlan.title)
|
|
2781
|
-
request.title = llmPlan.title;
|
|
2782
|
-
if (llmPlan.description)
|
|
2783
|
-
request.description = llmPlan.description;
|
|
2784
|
-
if (llmPlan.cadence)
|
|
2785
|
-
request.cadence = llmPlan.cadence;
|
|
2786
|
-
if (llmPlan.supportStrategy)
|
|
2787
|
-
request.supportStrategy = llmPlan.supportStrategy;
|
|
2788
|
-
if (llmPlan.successCriteria)
|
|
2789
|
-
request.successCriteria = llmPlan.successCriteria;
|
|
2790
|
-
if (llmPlan.groundingState) {
|
|
2791
|
-
request.metadata = mergeGoalMetadataWithGrounding({
|
|
2792
|
-
metadata: target.goal.metadata,
|
|
2793
|
-
nowIso: new Date().toISOString(),
|
|
2794
|
-
plan: {
|
|
2795
|
-
cadence: llmPlan.cadence,
|
|
2796
|
-
confidence: llmPlan.confidence,
|
|
2797
|
-
evaluationSummary: llmPlan.evaluationSummary,
|
|
2798
|
-
groundingState: llmPlan.groundingState,
|
|
2799
|
-
missingCriticalFields: llmPlan.missingCriticalFields,
|
|
2800
|
-
successCriteria: llmPlan.successCriteria ?? target.goal.successCriteria,
|
|
2801
|
-
targetDomain: llmPlan.targetDomain,
|
|
2802
|
-
},
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
if (request.title === undefined &&
|
|
2807
|
-
request.description === undefined &&
|
|
2808
|
-
request.cadence === undefined &&
|
|
2809
|
-
request.supportStrategy === undefined &&
|
|
2810
|
-
request.successCriteria === undefined &&
|
|
2811
|
-
request.metadata === undefined) {
|
|
2812
|
-
return {
|
|
2813
|
-
success: false,
|
|
2814
|
-
text: `Tell me what to change about "${target.goal.title}" and I'll update it.`,
|
|
2815
|
-
};
|
|
2816
|
-
}
|
|
2817
|
-
const updated = await service.updateGoal(target.goal.id, request);
|
|
2818
|
-
const fallback = `Updated goal "${updated.goal.title}".`;
|
|
2819
|
-
return {
|
|
2820
|
-
success: true,
|
|
2821
|
-
text: await renderLifeActionReply({
|
|
2822
|
-
runtime,
|
|
2823
|
-
message,
|
|
2824
|
-
state,
|
|
2825
|
-
intent,
|
|
2826
|
-
scenario: "updated_goal",
|
|
2827
|
-
fallback,
|
|
2828
|
-
context: {
|
|
2829
|
-
previousTitle: target.goal.title,
|
|
2830
|
-
updated: {
|
|
2831
|
-
title: updated.goal.title,
|
|
2832
|
-
},
|
|
2833
|
-
},
|
|
2834
|
-
}),
|
|
2835
|
-
data: toActionData(updated),
|
|
2836
|
-
};
|
|
2837
|
-
}
|
|
2838
|
-
if (operation === "delete_definition") {
|
|
2839
|
-
const target = await resolveDefinition(service, targetName, domain);
|
|
2840
|
-
if (!target)
|
|
2841
|
-
return {
|
|
2842
|
-
success: false,
|
|
2843
|
-
text: "I could not find that item to delete.",
|
|
2844
|
-
};
|
|
2845
|
-
await service.deleteDefinition(target.definition.id);
|
|
2846
|
-
const fallback = `Deleted "${target.definition.title}" and its occurrences.`;
|
|
2847
|
-
return {
|
|
2848
|
-
success: true,
|
|
2849
|
-
text: await renderLifeActionReply({
|
|
2850
|
-
runtime,
|
|
2851
|
-
message,
|
|
2852
|
-
state,
|
|
2853
|
-
intent,
|
|
2854
|
-
scenario: "deleted_definition",
|
|
2855
|
-
fallback,
|
|
2856
|
-
context: {
|
|
2857
|
-
deleted: {
|
|
2858
|
-
title: target.definition.title,
|
|
2859
|
-
},
|
|
2860
|
-
},
|
|
2861
|
-
}),
|
|
2862
|
-
};
|
|
2863
|
-
}
|
|
2864
|
-
if (operation === "delete_goal") {
|
|
2865
|
-
const target = await resolveGoal(service, targetName, domain);
|
|
2866
|
-
if (!target)
|
|
2867
|
-
return {
|
|
2868
|
-
success: false,
|
|
2869
|
-
text: "I could not find that goal to delete.",
|
|
2870
|
-
};
|
|
2871
|
-
await service.deleteGoal(target.goal.id);
|
|
2872
|
-
const fallback = `Deleted goal "${target.goal.title}".`;
|
|
2873
|
-
return {
|
|
2874
|
-
success: true,
|
|
2875
|
-
text: await renderLifeActionReply({
|
|
2876
|
-
runtime,
|
|
2877
|
-
message,
|
|
2878
|
-
state,
|
|
2879
|
-
intent,
|
|
2880
|
-
scenario: "deleted_goal",
|
|
2881
|
-
fallback,
|
|
2882
|
-
context: {
|
|
2883
|
-
deleted: {
|
|
2884
|
-
title: target.goal.title,
|
|
2885
|
-
},
|
|
2886
|
-
},
|
|
2887
|
-
}),
|
|
2888
|
-
};
|
|
2889
|
-
}
|
|
2890
|
-
if (operation === "complete_occurrence") {
|
|
2891
|
-
const { match: target, ambiguousCandidates } = await resolveOccurrenceWithIntentFallback({
|
|
2892
|
-
service,
|
|
2893
|
-
target: targetName,
|
|
2894
|
-
domain,
|
|
2895
|
-
intent,
|
|
2896
|
-
operation,
|
|
2897
|
-
});
|
|
2898
|
-
if (!target) {
|
|
2899
|
-
if (ambiguousCandidates.length > 0) {
|
|
2900
|
-
return {
|
|
2901
|
-
success: false,
|
|
2902
|
-
text: `Multiple items match — which one?\n${ambiguousCandidates.map((t) => ` - ${t}`).join("\n")}`,
|
|
2903
|
-
};
|
|
2904
|
-
}
|
|
2905
|
-
return {
|
|
2906
|
-
success: false,
|
|
2907
|
-
text: "I could not find that active item to complete.",
|
|
2908
|
-
};
|
|
2909
|
-
}
|
|
2910
|
-
const completed = await service.completeOccurrence(target.id, {
|
|
2911
|
-
note: detailString(details, "note"),
|
|
2912
|
-
});
|
|
2913
|
-
const fallback = `Marked "${completed.title}" done.`;
|
|
2914
|
-
return {
|
|
2915
|
-
success: true,
|
|
2916
|
-
text: await renderLifeActionReply({
|
|
2917
|
-
runtime,
|
|
2918
|
-
message,
|
|
2919
|
-
state,
|
|
2920
|
-
intent,
|
|
2921
|
-
scenario: "completed_occurrence",
|
|
2922
|
-
fallback,
|
|
2923
|
-
context: {
|
|
2924
|
-
completed: {
|
|
2925
|
-
title: completed.title,
|
|
2926
|
-
},
|
|
2927
|
-
note: detailString(details, "note"),
|
|
2928
|
-
},
|
|
2929
|
-
}),
|
|
2930
|
-
data: toActionData(completed),
|
|
2931
|
-
};
|
|
2932
|
-
}
|
|
2933
|
-
if (operation === "skip_occurrence") {
|
|
2934
|
-
const { match: target, ambiguousCandidates } = await resolveOccurrenceWithIntentFallback({
|
|
2935
|
-
service,
|
|
2936
|
-
target: targetName,
|
|
2937
|
-
domain,
|
|
2938
|
-
intent,
|
|
2939
|
-
operation,
|
|
2940
|
-
});
|
|
2941
|
-
if (!target) {
|
|
2942
|
-
if (ambiguousCandidates.length > 0) {
|
|
2943
|
-
return {
|
|
2944
|
-
success: false,
|
|
2945
|
-
text: `Multiple items match — which one?\n${ambiguousCandidates.map((t) => ` - ${t}`).join("\n")}`,
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
return {
|
|
2949
|
-
success: false,
|
|
2950
|
-
text: "I could not find that active item to skip.",
|
|
2951
|
-
};
|
|
2952
|
-
}
|
|
2953
|
-
const skipped = await service.skipOccurrence(target.id);
|
|
2954
|
-
const fallback = `Skipped "${skipped.title}".`;
|
|
2955
|
-
return {
|
|
2956
|
-
success: true,
|
|
2957
|
-
text: await renderLifeActionReply({
|
|
2958
|
-
runtime,
|
|
2959
|
-
message,
|
|
2960
|
-
state,
|
|
2961
|
-
intent,
|
|
2962
|
-
scenario: "skipped_occurrence",
|
|
2963
|
-
fallback,
|
|
2964
|
-
context: {
|
|
2965
|
-
skipped: {
|
|
2966
|
-
title: skipped.title,
|
|
2967
|
-
},
|
|
2968
|
-
},
|
|
2969
|
-
}),
|
|
2970
|
-
data: toActionData(skipped),
|
|
2971
|
-
};
|
|
2972
|
-
}
|
|
2973
|
-
if (operation === "snooze_occurrence") {
|
|
2974
|
-
const { match: target, ambiguousCandidates } = await resolveOccurrenceWithIntentFallback({
|
|
2975
|
-
service,
|
|
2976
|
-
target: targetName,
|
|
2977
|
-
domain,
|
|
2978
|
-
intent,
|
|
2979
|
-
operation,
|
|
2980
|
-
});
|
|
2981
|
-
if (!target) {
|
|
2982
|
-
if (ambiguousCandidates.length > 0) {
|
|
2983
|
-
return {
|
|
2984
|
-
success: false,
|
|
2985
|
-
text: `Multiple items match — which one?\n${ambiguousCandidates.map((t) => ` - ${t}`).join("\n")}`,
|
|
2986
|
-
};
|
|
2987
|
-
}
|
|
2988
|
-
return {
|
|
2989
|
-
success: false,
|
|
2990
|
-
text: "I could not find that active item to snooze.",
|
|
2991
|
-
};
|
|
2992
|
-
}
|
|
2993
|
-
const preset = detailString(details, "preset");
|
|
2994
|
-
const minutes = detailNumber(details, "minutes");
|
|
2995
|
-
const snoozed = await service.snoozeOccurrence(target.id, {
|
|
2996
|
-
preset,
|
|
2997
|
-
minutes,
|
|
2998
|
-
});
|
|
2999
|
-
const fallback = `Snoozed "${snoozed.title}".`;
|
|
3000
|
-
return {
|
|
3001
|
-
success: true,
|
|
3002
|
-
text: await renderLifeActionReply({
|
|
3003
|
-
runtime,
|
|
3004
|
-
message,
|
|
3005
|
-
state,
|
|
3006
|
-
intent,
|
|
3007
|
-
scenario: "snoozed_occurrence",
|
|
3008
|
-
fallback,
|
|
3009
|
-
context: {
|
|
3010
|
-
snoozed: {
|
|
3011
|
-
title: snoozed.title,
|
|
3012
|
-
},
|
|
3013
|
-
preset: preset ?? null,
|
|
3014
|
-
minutes: minutes ?? null,
|
|
3015
|
-
},
|
|
3016
|
-
}),
|
|
3017
|
-
data: toActionData(snoozed),
|
|
3018
|
-
};
|
|
3019
|
-
}
|
|
3020
|
-
if (operation === "review_goal") {
|
|
3021
|
-
const target = await resolveGoal(service, targetName, domain);
|
|
3022
|
-
if (!target)
|
|
3023
|
-
return {
|
|
3024
|
-
success: false,
|
|
3025
|
-
text: "I could not find that goal to review.",
|
|
3026
|
-
};
|
|
3027
|
-
const review = await service.reviewGoal(target.goal.id);
|
|
3028
|
-
return {
|
|
3029
|
-
success: true,
|
|
3030
|
-
text: review.summary.explanation,
|
|
3031
|
-
data: toActionData(review),
|
|
3032
|
-
};
|
|
3033
|
-
}
|
|
3034
|
-
if (operation === "set_reminder_preference") {
|
|
3035
|
-
const reminderIntensityPlan = await extractReminderIntensityWithLlm({
|
|
3036
|
-
runtime,
|
|
3037
|
-
intent,
|
|
3038
|
-
});
|
|
3039
|
-
if (reminderIntensityPlan.intensity === "unknown") {
|
|
3040
|
-
return {
|
|
3041
|
-
success: false,
|
|
3042
|
-
text: "I need to know whether you want reminders minimal, normal, persistent, or high priority only.",
|
|
3043
|
-
};
|
|
3044
|
-
}
|
|
3045
|
-
const intensity = reminderIntensityPlan.intensity;
|
|
3046
|
-
const target = await resolveDefinitionFromIntent(service, targetName, intent, domain);
|
|
3047
|
-
const request = {
|
|
3048
|
-
intensity,
|
|
3049
|
-
definitionId: target?.definition.id ?? null,
|
|
3050
|
-
note: chatText || intent,
|
|
3051
|
-
};
|
|
3052
|
-
const preference = await service.setReminderPreference(request);
|
|
3053
|
-
if (target) {
|
|
3054
|
-
const fallback = intensity === "high_priority_only"
|
|
3055
|
-
? `Reminder intensity for "${target.definition.title}" is now high priority only.`
|
|
3056
|
-
: `Reminder intensity for "${target.definition.title}" is now ${describeReminderIntensity(preference.effective.intensity)}.`;
|
|
3057
|
-
return {
|
|
3058
|
-
success: true,
|
|
3059
|
-
text: await renderLifeActionReply({
|
|
3060
|
-
runtime,
|
|
3061
|
-
message,
|
|
3062
|
-
state,
|
|
3063
|
-
intent,
|
|
3064
|
-
scenario: "set_reminder_preference",
|
|
3065
|
-
fallback,
|
|
3066
|
-
context: {
|
|
3067
|
-
scope: "definition",
|
|
3068
|
-
targetTitle: target.definition.title,
|
|
3069
|
-
intensity: preference.effective.intensity,
|
|
3070
|
-
},
|
|
3071
|
-
}),
|
|
3072
|
-
data: toActionData(preference),
|
|
3073
|
-
};
|
|
3074
|
-
}
|
|
3075
|
-
const fallback = intensity === "high_priority_only"
|
|
3076
|
-
? "Global LifeOps reminders are now high priority only."
|
|
3077
|
-
: `Global LifeOps reminders are now ${describeReminderIntensity(preference.effective.intensity)}.`;
|
|
3078
|
-
return {
|
|
3079
|
-
success: true,
|
|
3080
|
-
text: await renderLifeActionReply({
|
|
3081
|
-
runtime,
|
|
3082
|
-
message,
|
|
3083
|
-
state,
|
|
3084
|
-
intent,
|
|
3085
|
-
scenario: "set_reminder_preference",
|
|
3086
|
-
fallback,
|
|
3087
|
-
context: {
|
|
3088
|
-
scope: "global",
|
|
3089
|
-
intensity: preference.effective.intensity,
|
|
3090
|
-
},
|
|
3091
|
-
}),
|
|
3092
|
-
data: toActionData(preference),
|
|
3093
|
-
};
|
|
3094
|
-
}
|
|
3095
|
-
if (operation === "capture_phone") {
|
|
3096
|
-
const phoneNumber = detailString(details, "phoneNumber") ?? params.title;
|
|
3097
|
-
if (!phoneNumber)
|
|
3098
|
-
return {
|
|
3099
|
-
success: false,
|
|
3100
|
-
text: "I need a phone number to set up SMS or voice contact.",
|
|
3101
|
-
};
|
|
3102
|
-
const allowSms = detailBoolean(details, "allowSms") ?? true;
|
|
3103
|
-
const allowVoice = detailBoolean(details, "allowVoice") ?? false;
|
|
3104
|
-
const result = await service.capturePhoneConsent({
|
|
3105
|
-
phoneNumber,
|
|
3106
|
-
consentGiven: true,
|
|
3107
|
-
allowSms,
|
|
3108
|
-
allowVoice,
|
|
3109
|
-
privacyClass: "private",
|
|
3110
|
-
});
|
|
3111
|
-
const channels = [];
|
|
3112
|
-
if (allowSms)
|
|
3113
|
-
channels.push("SMS");
|
|
3114
|
-
if (allowVoice)
|
|
3115
|
-
channels.push("voice calls");
|
|
3116
|
-
const fallback = `Phone number ${result.phoneNumber} saved. Enabled for: ${channels.join(" and ") || "reminders"}.`;
|
|
3117
|
-
return {
|
|
3118
|
-
success: true,
|
|
3119
|
-
text: await renderLifeActionReply({
|
|
3120
|
-
runtime,
|
|
3121
|
-
message,
|
|
3122
|
-
state,
|
|
3123
|
-
intent,
|
|
3124
|
-
scenario: "captured_phone",
|
|
3125
|
-
fallback,
|
|
3126
|
-
context: {
|
|
3127
|
-
phoneNumber: result.phoneNumber,
|
|
3128
|
-
channels,
|
|
3129
|
-
},
|
|
3130
|
-
}),
|
|
3131
|
-
data: toActionData(result),
|
|
3132
|
-
};
|
|
3133
|
-
}
|
|
3134
|
-
if (operation === "configure_escalation") {
|
|
3135
|
-
const target = await resolveDefinition(service, targetName, domain);
|
|
3136
|
-
if (!target)
|
|
3137
|
-
return {
|
|
3138
|
-
success: false,
|
|
3139
|
-
text: "I could not find that item to configure its reminders.",
|
|
3140
|
-
};
|
|
3141
|
-
const rawSteps = detailArray(details, "steps") ??
|
|
3142
|
-
detailArray(details, "escalationSteps");
|
|
3143
|
-
const steps = rawSteps
|
|
3144
|
-
? rawSteps
|
|
3145
|
-
.filter((s) => typeof s === "object" && s !== null)
|
|
3146
|
-
.map((s) => ({
|
|
3147
|
-
channel: String(s.channel ?? "in_app"),
|
|
3148
|
-
offsetMinutes: typeof s.offsetMinutes === "number" ? s.offsetMinutes : 0,
|
|
3149
|
-
label: typeof s.label === "string"
|
|
3150
|
-
? s.label
|
|
3151
|
-
: String(s.channel ?? "reminder"),
|
|
3152
|
-
}))
|
|
3153
|
-
: [{ channel: "in_app", offsetMinutes: 0, label: "In-app reminder" }];
|
|
3154
|
-
const updated = await service.updateDefinition(target.definition.id, {
|
|
3155
|
-
ownership,
|
|
3156
|
-
reminderPlan: { steps },
|
|
3157
|
-
});
|
|
3158
|
-
const summary = steps
|
|
3159
|
-
.map((s) => `${s.channel} at +${s.offsetMinutes}m`)
|
|
3160
|
-
.join(", ");
|
|
3161
|
-
const fallback = `Updated reminder plan for "${updated.definition.title}": ${summary}.`;
|
|
3162
|
-
return {
|
|
3163
|
-
success: true,
|
|
3164
|
-
text: await renderLifeActionReply({
|
|
3165
|
-
runtime,
|
|
3166
|
-
message,
|
|
3167
|
-
state,
|
|
3168
|
-
intent,
|
|
3169
|
-
scenario: "configured_escalation",
|
|
3170
|
-
fallback,
|
|
3171
|
-
context: {
|
|
3172
|
-
targetTitle: updated.definition.title,
|
|
3173
|
-
steps,
|
|
3174
|
-
},
|
|
3175
|
-
}),
|
|
3176
|
-
data: toActionData(updated),
|
|
3177
|
-
};
|
|
3178
|
-
}
|
|
3179
|
-
return {
|
|
3180
|
-
success: false,
|
|
3181
|
-
text: "I didn't understand that life management request.",
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
|
-
catch (err) {
|
|
3185
|
-
if (err instanceof LifeOpsServiceError) {
|
|
3186
|
-
const fallback = buildLifeServiceErrorFallback(err, intent);
|
|
3187
|
-
return {
|
|
3188
|
-
success: false,
|
|
3189
|
-
text: await renderLifeActionReply({
|
|
3190
|
-
runtime,
|
|
3191
|
-
message,
|
|
3192
|
-
state,
|
|
3193
|
-
intent,
|
|
3194
|
-
scenario: "service_error",
|
|
3195
|
-
fallback,
|
|
3196
|
-
context: {
|
|
3197
|
-
status: err.status,
|
|
3198
|
-
operation,
|
|
3199
|
-
},
|
|
3200
|
-
}),
|
|
3201
|
-
};
|
|
3202
|
-
}
|
|
3203
|
-
throw err;
|
|
3204
|
-
}
|
|
3205
|
-
},
|
|
3206
|
-
parameters: [
|
|
3207
|
-
{
|
|
3208
|
-
name: "action",
|
|
3209
|
-
description: "What kind of life operation to perform.",
|
|
3210
|
-
required: false,
|
|
3211
|
-
schema: {
|
|
3212
|
-
type: "string",
|
|
3213
|
-
enum: [
|
|
3214
|
-
"create",
|
|
3215
|
-
"create_goal",
|
|
3216
|
-
"update",
|
|
3217
|
-
"update_goal",
|
|
3218
|
-
"delete",
|
|
3219
|
-
"delete_goal",
|
|
3220
|
-
"complete",
|
|
3221
|
-
"skip",
|
|
3222
|
-
"snooze",
|
|
3223
|
-
"review",
|
|
3224
|
-
"phone",
|
|
3225
|
-
"escalation",
|
|
3226
|
-
"reminder_preference",
|
|
3227
|
-
"calendar",
|
|
3228
|
-
"next_event",
|
|
3229
|
-
"email",
|
|
3230
|
-
"overview",
|
|
3231
|
-
],
|
|
3232
|
-
},
|
|
3233
|
-
},
|
|
3234
|
-
{
|
|
3235
|
-
name: "intent",
|
|
3236
|
-
description: 'Natural language description of what to do. Examples: "create a daily brushing habit for morning and night", "snooze brushing for 30 minutes", "what\'s on my calendar today".',
|
|
3237
|
-
required: false,
|
|
3238
|
-
schema: { type: "string" },
|
|
3239
|
-
},
|
|
3240
|
-
{
|
|
3241
|
-
name: "title",
|
|
3242
|
-
description: "Name for a new item, or the name of an existing item to act on.",
|
|
3243
|
-
required: false,
|
|
3244
|
-
schema: { type: "string" },
|
|
3245
|
-
},
|
|
3246
|
-
{
|
|
3247
|
-
name: "target",
|
|
3248
|
-
description: "Name or ID of an existing item when different from title (e.g., when renaming).",
|
|
3249
|
-
required: false,
|
|
3250
|
-
schema: { type: "string" },
|
|
3251
|
-
},
|
|
3252
|
-
{
|
|
3253
|
-
name: "details",
|
|
3254
|
-
description: "Structured data when needed. May include: cadence (schedule object), kind (task/habit/routine), description, priority, progressionRule, reminderPlan, confirmed (boolean when the user explicitly approves a previewed create), preset (snooze preset like 15m/30m/1h/tonight/tomorrow_morning), minutes (snooze minutes), phoneNumber, allowSms, allowVoice, steps (escalation steps array), goalId, goalTitle, supportStrategy, successCriteria, note, limit, domain (user_lifeops/agent_ops), or reminder preference targeting.",
|
|
3255
|
-
required: false,
|
|
3256
|
-
schema: { type: "object" },
|
|
3257
|
-
},
|
|
3258
|
-
],
|
|
3259
|
-
examples: [
|
|
3260
|
-
[
|
|
3261
|
-
{
|
|
3262
|
-
name: "{{name1}}",
|
|
3263
|
-
content: {
|
|
3264
|
-
text: "help me remember to drink water",
|
|
3265
|
-
},
|
|
3266
|
-
},
|
|
3267
|
-
{
|
|
3268
|
-
name: "{{agentName}}",
|
|
3269
|
-
content: {
|
|
3270
|
-
text: 'I can set up a "Drink water" habit with a reasonable daytime default cadence. Confirm and I\'ll save it.',
|
|
3271
|
-
actions: ["LIFE"],
|
|
3272
|
-
},
|
|
3273
|
-
},
|
|
3274
|
-
],
|
|
3275
|
-
[
|
|
3276
|
-
{
|
|
3277
|
-
name: "{{name1}}",
|
|
3278
|
-
content: {
|
|
3279
|
-
text: "help me remember to stretch during the day",
|
|
3280
|
-
},
|
|
3281
|
-
},
|
|
3282
|
-
{
|
|
3283
|
-
name: "{{agentName}}",
|
|
3284
|
-
content: {
|
|
3285
|
-
text: 'I can set up a "Stretch" habit with daytime stretch-break defaults. Confirm and I\'ll save it.',
|
|
3286
|
-
actions: ["LIFE"],
|
|
3287
|
-
},
|
|
3288
|
-
},
|
|
3289
|
-
],
|
|
3290
|
-
[
|
|
3291
|
-
{
|
|
3292
|
-
name: "{{name1}}",
|
|
3293
|
-
content: {
|
|
3294
|
-
text: "please remind me about my Invisalign on weekdays after lunch",
|
|
3295
|
-
},
|
|
3296
|
-
},
|
|
3297
|
-
{
|
|
3298
|
-
name: "{{agentName}}",
|
|
3299
|
-
content: {
|
|
3300
|
-
text: "I can set up a weekday-after-lunch Invisalign habit. Confirm and I'll save it.",
|
|
3301
|
-
actions: ["LIFE"],
|
|
3302
|
-
},
|
|
3303
|
-
},
|
|
3304
|
-
],
|
|
3305
|
-
[
|
|
3306
|
-
{
|
|
3307
|
-
name: "{{name1}}",
|
|
3308
|
-
content: {
|
|
3309
|
-
text: "recuérdame cepillarme los dientes por la mañana y por la noche",
|
|
3310
|
-
},
|
|
3311
|
-
},
|
|
3312
|
-
{
|
|
3313
|
-
name: "{{agentName}}",
|
|
3314
|
-
content: {
|
|
3315
|
-
text: 'Puedo guardar el hábito "Brush teeth" para la mañana y la noche. Confirma y lo guardo.',
|
|
3316
|
-
actions: ["LIFE"],
|
|
3317
|
-
},
|
|
3318
|
-
},
|
|
3319
|
-
],
|
|
3320
|
-
[
|
|
3321
|
-
{
|
|
3322
|
-
name: "{{name1}}",
|
|
3323
|
-
content: {
|
|
3324
|
-
text: "help me brush my teeth at 8 am and 9 pm every day",
|
|
3325
|
-
},
|
|
3326
|
-
},
|
|
3327
|
-
{
|
|
3328
|
-
name: "{{agentName}}",
|
|
3329
|
-
content: {
|
|
3330
|
-
text: 'I can set up a habit named "Brush teeth" for 8 am and 9 pm daily. Confirm and I\'ll save it.',
|
|
3331
|
-
actions: ["LIFE"],
|
|
3332
|
-
},
|
|
3333
|
-
},
|
|
3334
|
-
],
|
|
3335
|
-
[
|
|
3336
|
-
{
|
|
3337
|
-
name: "{{name1}}",
|
|
3338
|
-
content: {
|
|
3339
|
-
text: "what life ops tasks are still left for today?",
|
|
3340
|
-
},
|
|
3341
|
-
},
|
|
3342
|
-
{
|
|
3343
|
-
name: "{{agentName}}",
|
|
3344
|
-
content: {
|
|
3345
|
-
text: "You have 2 LifeOps tasks left for today: call mom and pay rent.",
|
|
3346
|
-
actions: ["LIFE"],
|
|
3347
|
-
},
|
|
3348
|
-
},
|
|
3349
|
-
{
|
|
3350
|
-
name: "{{name1}}",
|
|
3351
|
-
content: {
|
|
3352
|
-
text: "anything else in my life ops list i need to get done today?",
|
|
3353
|
-
},
|
|
3354
|
-
},
|
|
3355
|
-
{
|
|
3356
|
-
name: "{{agentName}}",
|
|
3357
|
-
content: {
|
|
3358
|
-
text: "You have 1 LifeOps task left for today: pay rent.",
|
|
3359
|
-
actions: ["LIFE"],
|
|
3360
|
-
},
|
|
3361
|
-
},
|
|
3362
|
-
],
|
|
3363
|
-
[
|
|
3364
|
-
{
|
|
3365
|
-
name: "{{name1}}",
|
|
3366
|
-
content: {
|
|
3367
|
-
text: "remind me less about brush teeth",
|
|
3368
|
-
},
|
|
3369
|
-
},
|
|
3370
|
-
{
|
|
3371
|
-
name: "{{agentName}}",
|
|
3372
|
-
content: {
|
|
3373
|
-
text: 'Reminder intensity for "Brush teeth" is now minimal.',
|
|
3374
|
-
actions: ["LIFE"],
|
|
3375
|
-
},
|
|
3376
|
-
},
|
|
3377
|
-
],
|
|
3378
|
-
],
|
|
3379
|
-
};
|
|
1
|
+
export * from "@elizaos/app-lifeops/actions/life";
|