elizaos--eliza
426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
235 行
10 KiB
Markdown
235 行
10 KiB
Markdown
# `@elizaos/plugin-personal-assistant`
|
|
|
|
LifeOps is the elizaOS app that runs the user's day: routines, goals,
|
|
calendar, email, messaging, follow-ups with people, blockers, watchers, and
|
|
the operational glue around them. This README is the architecture summary
|
|
for contributors.
|
|
|
|
## Scheduled items, not generic tasks
|
|
|
|
Every reminder, check-in, follow-up, watcher, recap, approval surface, and
|
|
nag-the-user-when-they-go-quiet flow is a **LifeOps scheduled item** stored
|
|
as a `ScheduledTask` record and owned by the runner at
|
|
`plugins/plugin-scheduling/src/scheduled-task/runner.ts` (the personal-assistant
|
|
side wires it in via `src/lifeops/scheduled-task/{service,scheduler,runtime-wiring}.ts`).
|
|
There is no second LifeOps scheduling mechanism.
|
|
|
|
`ScheduledTask` is intentionally not the repository-wide "task" primitive.
|
|
Core runtime tasks are persisted `Task` rows handled by `TaskService`; coding
|
|
agent work is orchestrator/task-coordinator state; project and feature tasks
|
|
may have their own plugin-owned records. LifeOps integrates with those
|
|
surfaces through public plugin/runtime contracts instead of importing or
|
|
owning them as LifeOps primitives.
|
|
|
|
The shape:
|
|
|
|
```ts
|
|
interface ScheduledTask {
|
|
taskId: string;
|
|
kind: "reminder" | "checkin" | "followup" | "approval" | "recap" | "watcher" | "output" | "custom";
|
|
promptInstructions: string;
|
|
contextRequest?: { /* owner facts, entities, relationships, recent task states, event payload */ };
|
|
trigger: /* once | cron | interval | relative_to_anchor | during_window | event | manual | after_task */;
|
|
priority: "low" | "medium" | "high";
|
|
shouldFire?: { compose: "all" | "any" | "first_deny"; gates: Array<{ kind: string; params? }> };
|
|
completionCheck?: { kind: string; params?: ...; followupAfterMinutes? };
|
|
escalation?: { ladderKey?: string; steps?: EscalationStep[] };
|
|
output?: { destination: ...; target?: string; persistAs?: ... };
|
|
pipeline?: { onComplete?, onSkip?, onFail? };
|
|
subject?: { kind: "entity" | "relationship" | "thread" | "document" | "calendar_event" | "self"; id: string };
|
|
idempotencyKey?: string;
|
|
respectsGlobalPause: boolean;
|
|
state: ScheduledTaskState;
|
|
source: "default_pack" | "user_chat" | "first_run" | "plugin";
|
|
createdBy: string;
|
|
ownerVisible: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
```
|
|
|
|
The runner pattern-matches **only** on the structural fields above
|
|
(`kind`, `trigger`, `shouldFire`, `completionCheck`, `pipeline`, `output`,
|
|
`subject`, `priority`, `respectsGlobalPause`). It never inspects
|
|
`promptInstructions` content. This is non-negotiable.
|
|
|
|
The frozen contract is defined in `src/lifeops/scheduled-task/types.ts`
|
|
(the runner imports `ScheduledTask` from there). `src/lifeops/wave1-types.ts`
|
|
is a slightly diverged copy consumed only by the `first-run` module.
|
|
|
|
### No-reply semantics
|
|
|
|
Completion timeouts are structural scheduler behavior, not prompt text. For
|
|
scheduled items that wait on a user reply, personal-assistant resolves a
|
|
persisted no-reply contract from `metadata.noReplyPolicy` plus
|
|
`metadata.noReplyState` and applies it in the single scheduler timeout pass:
|
|
|
|
```ts
|
|
metadata: {
|
|
noReplyPolicy?: {
|
|
maxRetries: number;
|
|
retryCadenceMinutes: number[];
|
|
terminalStatus: "skipped" | "expired" | "failed";
|
|
terminalReason: string;
|
|
sensitive: boolean;
|
|
allowCrossChannel: boolean;
|
|
allowNonOwnerNotification: boolean;
|
|
};
|
|
noReplyState?: {
|
|
retryCount: number;
|
|
lastTimedOutAt?: string;
|
|
nextRetryAt?: string;
|
|
terminalReason?: string;
|
|
terminalOutcome?: "skipped" | "expired" | "failed" | "denied";
|
|
};
|
|
}
|
|
```
|
|
|
|
Defaults are deliberately conservative:
|
|
|
|
- reminders retry once after 60 minutes, then `skipped`;
|
|
- check-ins retry once after 24 hours, then `expired`;
|
|
- non-sensitive approvals retry after 30 minutes and 2 hours, then `expired`;
|
|
- sensitive approvals retry once after 30 minutes, then expire with
|
|
`terminalOutcome: "denied"`;
|
|
- cross-channel escalation and non-owner notification default to `false`.
|
|
|
|
No no-reply path may auto-execute a sensitive output. Silence on money, data,
|
|
or external-send approvals fails closed. No-reply expiry is not a user skip, so
|
|
approval expiry does not fire `pipeline.onSkip`; authored skip pipelines remain
|
|
reserved for explicit skip transitions.
|
|
|
|
## Runtime layout
|
|
|
|
```
|
|
src/lifeops/
|
|
scheduled-task/ Spine: runner, state log, gate registry,
|
|
completion-check registry, escalation, runtime
|
|
wiring.
|
|
entities/ Entity primitive: store, merge engine, types.
|
|
relationships/ Relationship edges: store, observation
|
|
extraction, types.
|
|
registries/ AnchorRegistry, EventKindRegistry, FamilyRegistry,
|
|
BlockerRegistry, app/website blocker contributions.
|
|
signals/ ActivitySignalBus.
|
|
channels/ ChannelRegistry, priority-posture map, default
|
|
channel pack.
|
|
connectors/ ConnectorRegistry + per-connector contributions
|
|
(calendly, discord, duffel, google, imessage,
|
|
signal, telegram, twilio, whatsapp, x).
|
|
send-policy/ Per-connector send-policy contract + registry.
|
|
owner/ OwnerFactStore.
|
|
first-run/ FirstRunService, state store, customize
|
|
questions, replay.
|
|
pending-prompts/ PendingPromptsStore (the planner-visible
|
|
"questions waiting for the user" surface).
|
|
global-pause/ GlobalPauseStore.
|
|
handoff/ HandoffStore (per-room handoff state).
|
|
i18n/ MultilingualPromptRegistry.
|
|
graph-migration/ Migration into the entity/relationship graph.
|
|
seed-routine-migration/ Migration off legacy seed routines.
|
|
...other LifeOps-owned helpers (calendar, email, messaging, payments,
|
|
subscriptions, assistant workflows, etc.)
|
|
```
|
|
|
|
## Default packs
|
|
|
|
Default packs are bundles of typed scheduled-item definitions compiled into
|
|
`ScheduledTask` records (and sometimes anchor-consolidation policies,
|
|
escalation ladders, autofill whitelists). LifeOps-owned packs live in
|
|
`src/default-packs/`:
|
|
|
|
- `daily-rhythm` — gm, gn, daily check-in.
|
|
- `morning-brief` — fired on `wake.confirmed`.
|
|
- `quiet-user-watcher` — daily watcher.
|
|
- `habit-starters` — eight habits, **offered** (not auto-seeded).
|
|
- `executive-assistant` — twenty-five scheduled records covering twenty
|
|
personal/executive assistant scenario families, **offered** (not auto-seeded).
|
|
- `inbox-triage-starter` — opt-in, gated on Gmail.
|
|
- `followup-starter` — watcher firing per overdue relationship.
|
|
- `autofill-whitelist-pack`, `consolidation-policies`, `escalation-ladders`
|
|
— policy-only packs.
|
|
|
|
`@elizaos/plugin-health` ships `bedtime`, `wake-up`, `sleep-recap` and
|
|
registers them when a health connector pairs.
|
|
|
|
### Adding a new default pack
|
|
|
|
1. Add a file under `src/default-packs/<name>.ts` that exports a
|
|
`DefaultPack` matching `registry-types.ts`.
|
|
2. Define `ReminderTaskDefinition`, `CheckInTaskDefinition`,
|
|
`WatcherTaskDefinition`, `ApprovalTaskDefinition`, `RecapTaskDefinition`,
|
|
or `OutputTaskDefinition` values and compile them with
|
|
`compileTaskDefinition` / `compileTaskDefinitions`. Pack files should not
|
|
construct raw `ScheduledTaskSeed` records.
|
|
3. Import and append it to `DEFAULT_PACKS` in `src/default-packs/index.ts`.
|
|
4. If the pack should be **auto-enabled**, list it in
|
|
`getDefaultEnabledPacks`. If it should be **offered** during first-run
|
|
customize, list it in `getOfferedDefaultPacks`. If neither, the pack
|
|
only seeds when invoked explicitly.
|
|
5. Run `bun run lint:default-packs` (also runs as `pretest`). The lint
|
|
rules are embedded in `scripts/lint-default-packs.mjs`. CI rejects packs
|
|
that violate them, including raw `ScheduledTask` construction.
|
|
6. Add a record-id constant export so consumers can target the records by
|
|
stable ID.
|
|
|
|
The runtime never seeds packs by name string-match; everything goes through
|
|
`getAllDefaultPacks()`.
|
|
|
|
## Knowledge graph
|
|
|
|
`EntityStore` (nodes) and `RelationshipStore` (edges) at
|
|
`src/lifeops/entities/` and `src/lifeops/relationships/`. The graph is
|
|
per-agent. The `entityId === "self"` row is bootstrapped on first use.
|
|
|
|
- **Cadence lives on the edge.** "Pat — every 14 days" is a
|
|
`Relationship`, not an `Entity` attribute. Cadence-bearing
|
|
`ScheduledTask`s use `subject.kind = "relationship"`.
|
|
- **Identities are observed.** `(platform, handle)` pairs route through
|
|
`observeIdentity`; the merge engine in `entities/merge.ts` collapses
|
|
entities with high-confidence identity matches. Manual merges go through
|
|
`POST /api/lifeops/entities/merge` and are audited.
|
|
- **REST surface** — routes live in `src/routes/`.
|
|
|
|
## Pause and handoff
|
|
|
|
- **Global pause** (`global-pause/store.ts`) — stops every
|
|
`ScheduledTask` with `respectsGlobalPause: true`. Toggleable via UI or
|
|
`/api/lifeops/app-state`.
|
|
- **Per-room handoff** (`handoff/store.ts`) — flips a multi-party room
|
|
into handoff after the agent says "I'll let you take it from here."
|
|
Typed resume conditions (`mention | explicit_resume | silence_minutes |
|
|
user_request_help`). The `RoomPolicyProvider` reads
|
|
`HandoffStore.status(roomId).active` and gates further agent
|
|
contributions.
|
|
|
|
## Plugin dependencies
|
|
|
|
LifeOps consumes `@elizaos/plugin-health` for sleep/circadian/health metrics,
|
|
screen-time action planning, health action planning, health-context formatting,
|
|
and health connector contributions.
|
|
The plugin contributes through the registries listed above (`AnchorRegistry`,
|
|
`ConnectorRegistry`, `FamilyRegistry`, default packs) and public factories such
|
|
as `createHealthActionRunner`, `createScreenTimeActionRunner`, and
|
|
`createHealthProvider`. LifeOps does not import directly into the health
|
|
internals; it consumes the plugin's public exports only. See
|
|
`plugins/plugin-health/README.md`.
|
|
|
|
## Cross-agent invariants
|
|
|
|
1. The runner never pattern-matches `promptInstructions`.
|
|
2. `subject.kind = "relationship"` for cadence-bearing tasks.
|
|
3. Identities are observed, not assigned.
|
|
4. Connectors and channels return typed `DispatchResult`. No `boolean`.
|
|
5. `shouldFire.gates` is always an array.
|
|
6. `acknowledged` ≠ `completed`. Pipeline `onComplete` only fires on
|
|
`completed`.
|
|
7. Snooze resets the escalation ladder.
|
|
8. Global pause skips tasks with `respectsGlobalPause: true`.
|
|
|
|
## Where to look next
|
|
|
|
- Frozen interface types: `src/lifeops/scheduled-task/types.ts`.
|
|
- Prompt-content lint rules: `scripts/lint-default-packs.mjs`.
|
|
- Health domain: `plugins/plugin-health/README.md`.
|
|
- REST routes: `src/routes/`.
|