{"id":"b0ee6f80-cf09-4a2b-aa38-c77890a16eee","entityType":"agent","slug":"clawhub-kasanuowa-cyber-girlfriend","name":"Cyber Girlfriend","canonicalUrl":"https://www.xpersona.co/agent/clawhub-kasanuowa-cyber-girlfriend","canonicalPath":"/agent/clawhub-kasanuowa-cyber-girlfriend","generatedAt":"2026-10-09T21:23:30.554Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":null},"description":"Owner-only proactive companion system","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 3.1K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s179sw03xjd5aqn751cpzenk4n83v551:cyber-girlfriend","sourceUrl":"https://clawhub.ai/kasanuowa/cyber-girlfriend","homepage":"https://clawhub.ai/kasanuowa/skills/cyber-girlfriend","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/kasanuowa/cyber-girlfriend","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/kasanuowa/skills/cyber-girlfriend","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":53,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Cyber Girlfriend technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":null},"stars":null,"forks":null,"downloads":3148,"packageName":null,"latestVersion":"2.2.0","tractionLabel":"3.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T09:30:52.667Z","lastCrawledAt":"2026-10-09T09:30:52.667Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T09:30:52.667Z","lastVerifiedAt":null,"highlights":[{"version":"2.2.0","createdAt":"2026-09-03T02:47:04.689Z","changelog":"OpenClaw automation compatibility: deterministic exact-argv presence commands, fresh dispatch sessions, explicit-contract WeChat fallback with retryable delivery failures, and lightweight daily-builder context. Adds explicit skill invocation, authorization previews, reversible pause/removal guidance, and release checks. 中文说明：适配新版 OpenClaw command automation，presence 直接执行固定脚本并为每次命中派生新会话，避开归档会话复用；微信渠道异常沿用显式投递合同兜底，失败可重试；日程任务启用轻量上下文。同时完善显式调用、操作前确认、可逆暂停与独立删除授权，并纳入发布校验。","fileCount":28,"zipByteSize":122621},{"version":"2.1.9","createdAt":"2026-06-10T12:37:46.274Z","changelog":"Text delivery now uses the deterministic --send-story wrapper path, keeping custom-channel delivery aligned with the external OpenClaw CLI and preserving wrapper-managed media delivery.","fileCount":28,"zipByteSize":112246},{"version":"2.1.6","createdAt":"2026-06-02T03:29:02.192Z","changelog":"Harden presence wrapper OpenClaw CLI child process CA handling; keep dispatch startup acknowledgement and launch diagnostics; verified with real matched-event delivery.","fileCount":29,"zipByteSize":105036},{"version":"2.1.5","createdAt":"2026-05-31T14:38:24.958Z","changelog":"Remove prompt wording that names concrete tools; keep mandatory real web-search requirements and sync live cron wording.","fileCount":29,"zipByteSize":102468},{"version":"2.1.4","createdAt":"2026-05-28T05:19:36.183Z","changelog":"Presence contract cleanup. Removed legacy visible mode field from prepare contracts and wrapper status output. Updated default prepare command to --stage prepare --no-record-pending. Kept old --mode heartbeat as hidden compatibility alias.","fileCount":29,"zipByteSize":102494},{"version":"2.1.3","createdAt":"2026-05-27T09:02:52.446Z","changelog":"2.1.3: wrapper-first presence cron, mandatory matched-event public web search, media events commit after text delivery, and cleaned setup/migration docs.","fileCount":29,"zipByteSize":100143},{"version":"2.1.0","createdAt":"2026-05-26T08:39:58.385Z","changelog":"Public release cleanup for the simplified presence companion. - Bumped the skill metadata to `2.1.0` because this release removes old runtime paths and substantially reshapes the publishable surface. - Removed git-local packaging assumptions; this skill is published through ClawHub, not from an embedded git repository. - Kept `.clawhubignore` as the authoritative publish boundary and added release checks that local maintenance Markdown and git artifacts stay out of the ClawHub package. - Preserved the mandatory internet-search material rule in the publishable cron and prompt templates without local profile examples. - Clarified first-time setup and upgrade migration so new installs search public materials before generating ordinary daily events. - Removed stale upgrade references to deleted JSON-to-Markdown helpers. - Cleaned the finished day-schedule example so it contains only schedule content, not generation constraints.","fileCount":28,"zipByteSize":92526},{"version":"2.0.3","createdAt":"2026-05-25T09:03:03.728Z","changelog":"Rewrote the skill entry as a quick-start landing page, removed an unreferenced reference file, synced the release smoke fixture with the runtime state schema, and cleaned a duplicate test import.","fileCount":31,"zipByteSize":109685}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s179sw03xjd5aqn751cpzenk4n83v551:cyber-girlfriend","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s179sw03xjd5aqn751cpzenk4n83v551:cyber-girlfriend` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/kasanuowa/cyber-girlfriend before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T21:23:30.551Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-kasanuowa-cyber-girlfriend/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":null},"readme":"Skill: Cyber Girlfriend\n\nOwner: kasanuowa\n\nSummary: Owner-only proactive companion system\n\nTags: companion:2.0.1, cron:2.0.1, latest:2.2.0, openclaw:2.0.1, persona:2.0.1\n\nVersion history:\n\nv2.2.0 | 2026-09-03T02:47:04.689Z | user\n\nOpenClaw automation compatibility: deterministic exact-argv presence commands, fresh dispatch sessions, explicit-contract WeChat fallback with retryable delivery failures, and lightweight daily-builder context. Adds explicit skill invocation, authorization previews, reversible pause/removal guidance, and release checks.\n中文说明：适配新版 OpenClaw command automation，presence 直接执行固定脚本并为每次命中派生新会话，避开归档会话复用；微信渠道异常沿用显式投递合同兜底，失败可重试；日程任务启用轻量上下文。同时完善显式调用、操作前确认、可逆暂停与独立删除授权，并纳入发布校验。\n\nv2.1.9 | 2026-06-10T12:37:46.274Z | user\n\nText delivery now uses the deterministic --send-story wrapper path, keeping custom-channel delivery aligned with the external OpenClaw CLI and preserving wrapper-managed media delivery.\n\nv2.1.6 | 2026-06-02T03:29:02.192Z | user\n\nHarden presence wrapper OpenClaw CLI child process CA handling; keep dispatch startup acknowledgement and launch diagnostics; verified with real matched-event delivery.\n\nv2.1.5 | 2026-05-31T14:38:24.958Z | user\n\nRemove prompt wording that names concrete tools; keep mandatory real web-search requirements and sync live cron wording.\n\nv2.1.4 | 2026-05-28T05:19:36.183Z | user\n\nPresence contract cleanup. Removed legacy visible mode field from prepare contracts and wrapper status output. Updated default prepare command to --stage prepare --no-record-pending. Kept old --mode heartbeat as hidden compatibility alias.\n\nv2.1.3 | 2026-05-27T09:02:52.446Z | user\n\n2.1.3: wrapper-first presence cron, mandatory matched-event public web search, media events commit after text delivery, and cleaned setup/migration docs.\n\nv2.1.0 | 2026-05-26T08:39:58.385Z | user\n\nPublic release cleanup for the simplified presence companion.\n- Bumped the skill metadata to `2.1.0` because this release removes old runtime paths and substantially reshapes the publishable surface.\n- Removed git-local packaging assumptions; this skill is published through ClawHub, not from an embedded git repository.\n- Kept `.clawhubignore` as the authoritative publish boundary and added release checks that local maintenance Markdown and git artifacts stay out of the ClawHub package.\n- Preserved the mandatory internet-search material rule in the publishable cron and prompt templates without local profile examples.\n- Clarified first-time setup and upgrade migration so new installs search public materials before generating ordinary daily events.\n- Removed stale upgrade references to deleted JSON-to-Markdown helpers.\n- Cleaned the finished day-schedule example so it contains only schedule content, not generation constraints.\n\nv2.0.3 | 2026-05-25T09:03:03.728Z | user\n\nRewrote the skill entry as a quick-start landing page, removed an unreferenced reference file, synced the release smoke fixture with the runtime state schema, and cleaned a duplicate test import.\n\nv2.0.2 | 2026-05-24T16:46:54.677Z | user\n\n2.0.2 publish readiness release: excludes local config/state/tests/maintenance files from ClawHub package, confirms OpenClaw async media callback contract, and revalidates the publishable release surface.\n\nv2.0.1 | 2026-05-24T09:21:38.033Z | user\n\n## 2.0.1\n\nPackaging cleanup release.\n\n- Excluded tests from the ClawHub package.\n- Removed local-path example text from publishable files.\n- Kept the 2.0.0 runtime behavior unchanged.\n- Revalidated the release surface before publishing.\n\nv2.0.0 | 2026-05-24T09:18:44.503Z | user\n\n## 2.0.0\n\nMajor 2.0 release for the owner-only cyber girlfriend presence companion.\n\n### Added\n\n- Added a single prepare-only presence runner: `scripts/companion_run.py --stage prepare --mode heartbeat`.\n- Added Markdown private-life assets for `character-profile.md`, `week-plan.md`, and `day-schedule.md`.\n- Added validators for character profile, week plan, day schedule, and the release surface.\n- Added 1.x legacy upgrade helpers for old config persona and old JSON private-life artifacts.\n- Added a compact turn contract schema and continuity life-log schema/example.\n\n### Changed\n\n- Moved default proactive delivery to `companion-presence`, which samples the current `day-schedule.md` event and commits state only after visible delivery succeeds.\n- Simplified `life_context` into pure factual material: speaker, today, event, delivery mood, and reality check.\n- Moved writing constraints out of runner output and into the presence cron template.\n- Updated presence writing rules to produce a first-person event story of at least 120 Chinese characters when a current event is active.\n- Updated week/day planning rules so events must be concrete and may include light drama, contrast, or small everyday surprises while staying believable.\n- Consolidated duplicate OpenClaw, heartbeat, cron wizard, and custom-event reference docs into focused integration references.\n\n### Removed\n\n- Removed the old `companion_ping.py` entrypoint and old multi-script prompt/render chain.\n- Removed default render/full mode, external render specs, async media completion, and `life-filter-policy.json`.\n- Removed old month/day JSON example assets from the default release surface.\n- Removed old four-slot visible content cron docs from the default install path.\n- Removed old 1.x release notes from the publishable reference surface.\n\n### Upgrade Notes\n\n- Existing 1.x installs should use the legacy upgrade docs and migration scripts before enabling the 2.0 presence cron.\n- Old `persona`, `month-plan.json`, `day-context.json`, and prepare/render cron payloads are migration inputs only.\n- New installs should start from `character-profile.md`, `week-plan.md`, `day-schedule.md`, `companion-state.json`, and `life-log.jsonl`.\n\nv1.3.2 | 2026-05-22T12:38:51.869Z | user\n\nHard gate native heartbeat needs_review renders: no final_message_contract is emitted, visible_delivery_allowed is false, and heartbeat falls back to HEARTBEAT_OK instead of leaking status narration.\n\nv1.3.1 | 2026-05-22T09:59:12.355Z | user\n\nPatch native heartbeat visibility so owner-facing replies never include render status, code blocks, Step labels, JSON snippets, or internal progress narration.\n\nv1.3.0 | 2026-05-22T07:20:33.747Z | user\n\nRelease 1.3.0: consolidate live cron into the companion_run prepare/render contract, simplify render specs, remove obsolete multi-script live cron dependencies, keep media contracts generic, and refresh setup/upgrade guidance.\n\nv1.2.0 | 2026-05-21T10:41:42.096Z | user\n\nHarden companion_run contract pipeline, fix malformed config handling, improve media intent detection, and strengthen release/test coverage\n\nv1.1.2 | 2026-05-20T10:02:29.825Z | user\n\nFix heartbeat documentation to use the OpenClaw 5.18 companion_run contract and remove stale legacy heartbeat script guidance.\n\nv1.1.1 | 2026-05-20T09:46:40.950Z | user\n\nAdapt OpenClaw 5.18 live cron flow: add compact runner contracts, async media completion guidance, migration/validation helpers, and keep user-defined media details out of the shared skill.\n\nv1.1.0 | 2026-05-19T09:52:35.312Z | user\n\n1.1.0: adds lightweight owner_profile onboarding and USER.md import guidance, owner/companion boundary prompts, dry-run-safe cron regression, external life filter policy, render spec schema, migration helper, and release validation gate.\n\nv0.6.0 | 2026-05-19T06:09:58.164Z | user\n\nMajor 0.6 release: standardized live cron v2 pipeline with companion_ping -> generate_life_prompt -> render_companion_message -> mark-sent, added private-life month/day context templates and schemas, hardened first-time setup and v2 migration guidance, cleaned publishable package to exclude local config/state/test artifacts.\n\nv1.0.0 | 2026-05-19T06:08:38.689Z | user\n\nMajor release: standardized live cron v2 pipeline with companion_ping -> generate_life_prompt -> render_companion_message -> mark-sent, added private-life month/day context templates and schemas, hardened first-time setup and v2 migration guidance, cleaned publishable package to exclude local config/state/test artifacts.\n\nv0.5.3 | 2026-05-17T16:42:58.312Z | user\n\nDeduplicate setup references, remove the redundant onboarding guide, and tighten the OpenClaw/live-cron docs without changing runtime behavior.\n\nv0.5.2 | 2026-05-17T16:19:47.738Z | user\n\nSlim down SKILL.md, move init/upgrade rules into a dedicated reference, and keep workflow routing clearer without changing runtime behavior.\n\nv0.5.1 | 2026-05-16T13:39:57.513Z | user\n\nExclude runtime-generated context files from the published package; behavior and docs unchanged from 0.5.0.\n\nv0.5.0 | 2026-05-16T13:37:38.749Z | user\n\nStandardize OpenClaw handler/onboarding flow, harden native heartbeat delivery proof, and align the publishable schema with runtime-derived profile output.\n\nv0.4.1 | 2026-05-15T14:35:21.813Z | user\n\nAdd dedicated heartbeat integration note; clarify native heartbeat in-session delivery and transcript-reconciled mark-sent guidance.\n\nv0.4.0 | 2026-05-15T06:54:48.515Z | user\n\n0.4.0 republishes the intended 0.4 line after the previous release was accidentally cut as 0.3.5. Highlights: native OpenClaw heartbeat is clearly separated from companion cron jobs; heartbeat templates recommend binding to the real owner DM session for transcript continuity; pending-send transcript reconciliation is documented instead of same-turn --mark-sent after final heartbeat replies; publish hygiene excludes local config, runtime state, backups, and Python caches; the bundled pacing script now supports macOS system Python 3.9 by postponing annotation evaluation.\n\nv0.3.5 | 2026-05-15T04:17:46.835Z | user\n\nNative OpenClaw heartbeat guidance and safer transcript-continuity templates. Adds pending-send reconciliation guidance, separates native heartbeat from companion cron jobs, and tightens publish hygiene.\n\nv0.3.4 | 2026-05-01T03:21:43.529Z | user\n\nTemplate refresh for safer OpenClaw companion setups.\n\nHighlights:\n- Recommend dedicated persistent companion sessions for live cron jobs (for example session:companion-owner).\n- Clarify that main + systemEvent remains legacy-compatible but can bleed into heartbeat or owner-chat context.\n- Refresh live cron templates to include explicit session routing and explicit delivery targets.\n- Align the top-level SKILL.md architecture guidance with the updated OpenClaw integration docs.\n\nNotes:\n- Documentation/template release only; pacing/state script behavior is unchanged.\n\nv0.3.3 | 2026-04-24T14:38:52.679Z | user\n\nFix companion heartbeat gateway health check to accept current OpenClaw 'Connectivity probe: ok' status output in addition to legacy 'RPC probe: ok'.\n\nv0.3.2 | 2026-04-10T02:48:39.467Z | user\n\nAdd copy-pasteable live cron templates, remove hard message-length caps, and let heartbeat occasionally weave in fresh topical shares when context fits.\n\nv0.3.1 | 2026-04-09T04:26:18.723Z | user\n\nRefined proactive delivery architecture: follow delivery config instead of current session; cooldown now records only after successful delivery via --mark-sent; heartbeat has its own cooldown bucket and is no longer blocked by once-per-day mode gating; removed owner_active_recently; added onboarding flow and starter cron blueprints for OpenClaw setup.\n\nv0.3.0 | 2026-04-08T06:30:21.735Z | user\n\n重构为双层架构：cron 管行为交付，脚本管状态上下文。支持 heartbeat 和自定义 mode，移除浏览器爬虫依赖改用 agent web search，脚本不再自行生成/发送消息。\n\nv0.2.2 | 2026-03-03T05:58:49.216Z | user\n\nAdd morning mode, configurable cron schedule sync, and docs/runtime fixes.\n\nv0.2.1 | 2026-03-02T06:58:46.561Z | user\n\nAdd transient model retry/fallback guidance and runtime retry config fields.\n\nv0.2.0 | 2026-03-02T05:37:51.490Z | user\n\nAdd config-driven runtime entrypoint for proactive companion behavior, OpenClaw derivation support, and skill-local state/source layout guidance.\n\nv0.1.0 | 2026-03-02T05:21:35.772Z | user\n\nInitial public release of a configurable cyber-girlfriend companion skill with owner-only proactive messaging, relationship heuristics, and optional X trending share-source support.\n\nArchive index:\n\nArchive v2.2.0: 28 files, 122621 bytes\n\nFiles: agents/openai.yaml (325b), assets/character-profile.example.md (4042b), assets/cyber-girlfriend.config.example.json (2974b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (5898b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3161b), references/agent-first-time-qa-template.md (4816b), references/configuration.md (9676b), references/contract-schema.md (4595b), references/first-time-setup.md (10491b), references/presence-integration.md (9817b), references/private-life-cron-templates.md (12335b), references/private-life-layer.md (5256b), references/private-life-prompt-templates.md (13275b), references/required-events-and-cron.md (3989b), references/script-contract-v2-migration.md (2231b), references/standard-init-upgrade-flow.md (13671b), scripts/companion_presence_tick.py (62162b), scripts/companion_run.py (81128b), scripts/migrate_config.py (12179b), scripts/validate_character_profile.py (4888b), scripts/validate_day_schedule.py (14666b), scripts/validate_release.py (35342b), skill-card.md (2936b), SKILL.md (14985b), _meta.json (135b)\n\nFile v2.2.0:SKILL.md\n\n---\nname: cyber-girlfriend\ndescription: Build or customize an owner-only proactive companion system with a cyber-girlfriend persona, Markdown private-life context, lightweight relationship memory, and OpenClaw presence cron delivery.\nmetadata:\n  version: \"2.2.0\"\n---\n\n# Cyber Girlfriend\n\nUse this skill when the user wants an owner-only proactive companion instead of a purely reactive assistant.\n\nThis skill gives the owner:\n- proactive companion messages sent on a real schedule\n- a core persona in `character-profile.md`\n- daily private-life context in `day-schedule.md`\n- configurable quiet hours and event-level pacing\n- lightweight continuity in `life-log.jsonl`\n- optional event media such as photos, audio, or video\n\n## Quick Start\n\nThis skill is meant to be set up by an agent, not by hand.\n\nIf the user wants the default setup, the simplest explicit invocation is:\n\n> Use $cyber-girlfriend to help me set up a cyber girlfriend.\n\nThe agent should then gather the minimum inputs, create or update the local files, wire the default cron jobs, and validate the install before claiming success.\n\n## What The User Needs\n\nFor a normal install, the user only needs:\n- an OpenClaw runtime\n- one working delivery route to the owner\n- a few persona and daily-life anchors\n\nThe user should not need to:\n- hand-write JSON\n- hand-write cron payloads\n- manually wire runner contracts\n- read every reference file before getting started\n\n## Default Setup Shape\n\nThe recommended starter setup is:\n- one daily schedule builder job that writes `day-schedule.md`\n- one `companion-presence` automation that runs the deterministic tick wrapper as an exact Gateway command payload\n- 1-4 optional life anchors that are written into `day-schedule.md`\n\nThose anchors are life facts, not guaranteed sends.\n\n## What The Agent Sets Up\n\nThe current default active path has two small steps:\n- `scripts/companion_presence_tick.py --config <CONFIG>`\n- inside that wrapper, `scripts/companion_run.py --stage prepare --no-record-pending`\n\nOn OpenClaw versions that support command automations, cron invokes the wrapper directly with an exact argv payload; no model turn is used merely to launch the script. The wrapper reads local state through the prepare runner and exits quietly when no current event should send. Only when prepare returns `status = \"ok\"` does it derive a fresh dispatch-scoped companion session from the prepared run id. That session writes the first-person story, but text delivery goes through `companion_presence_tick.py --send-story --story-stdin`, which reloads the saved delivery contract from the dispatch lock, sends with the external OpenClaw CLI, and commits state only after successful text delivery. If the matched event asks for media, media generation starts after `--send-story` succeeds and finishes asynchronously. The wrapper also starts a deterministic recent-media watcher for the same dispatch session, so generated media is delivered through the prepared delivery contract even if the native completion turn falls back to Codex internal UI. Cross-event continuity comes only from the local state files, so archived runtime sessions are never reused.\n\nThe default local files are:\n- `character-profile.md`\n- `day-schedule.md`\n- `companion-state.json`\n- `life-log.jsonl`\n\nLegacy 1.x inputs such as `persona`, `month-plan.json`, `day-context.json`, and the old multi-slot cron path are upgrade-only compatibility inputs, not the default product path.\n\n## Hard Rules\n\n- Do not run this skill through implicit discovery; the user must explicitly invoke `$cyber-girlfriend`.\n- Never hardcode secrets.\n- Keep proactive behavior owner-only unless the user explicitly wants broader scope.\n- Keep runtime-specific values in `config.local.json` or environment variables, not published defaults.\n- Keep the companion's core persona in `character-profile.md`; treat `config.local.json -> persona` as deprecated migration data.\n- Keep presence cron payloads thin; the cron should call `companion_presence_tick.py`, not duplicate long writing instructions in runtime configuration.\n- Prefer an exact argv command payload for `companion-presence`; do not spend an isolated model turn only to run the wrapper. Use the legacy thin agent-turn payload only when the installed OpenClaw does not support command automations.\n- Run `companion-build-day-schedule` with lightweight bootstrap context because its payload explicitly names every required project input.\n- User-defined required events are life anchors, not guaranteed message sends.\n- Day schedule events must keep `媒体信息`; leave it empty unless the matched event should produce photo, audio, video, or similar media.\n- Final user-visible companion text must be first person from the companion's perspective.\n- Do not expose internal JSON, code blocks, step names, debug output, local paths, account ids, channel ids, or session ids in owner-facing messages.\n- Before writing local config or state, running public-web search, creating/updating/enabling recurring jobs, or sending the first controlled verification message, preview the exact scope and wait for explicit user confirmation.\n- Pause is reversible: disable the exact builder and presence jobs without deleting local config or state. Permanently remove jobs only after a separate explicit request.\n- Do not claim setup or upgrade is complete before a real validation command passes.\n\n## Read This First For Real Setup Or Upgrade\n\nAlways read:\n- [references/standard-init-upgrade-flow.md](./references/standard-init-upgrade-flow.md)\n- [references/configuration.md](./references/configuration.md)\n- [references/contract-schema.md](./references/contract-schema.md)\n\n## Choose The Right Reference\n\n- first-time setup or rebuild:\n  - [references/first-time-setup.md](./references/first-time-setup.md)\n  - [references/agent-first-time-qa-template.md](./references/agent-first-time-qa-template.md)\n  - [references/required-events-and-cron.md](./references/required-events-and-cron.md)\n- OpenClaw runtime wiring:\n  - [references/presence-integration.md](./references/presence-integration.md)\n- private-life layer:\n  - [references/private-life-layer.md](./references/private-life-layer.md)\n  - [references/private-life-cron-templates.md](./references/private-life-cron-templates.md)\n  - [references/private-life-prompt-templates.md](./references/private-life-prompt-templates.md)\n- custom required event anchors:\n  - [references/required-events-and-cron.md](./references/required-events-and-cron.md)\n- legacy upgrades:\n  - [references/standard-init-upgrade-flow.md](./references/standard-init-upgrade-flow.md)\n  - [references/script-contract-v2-migration.md](./references/script-contract-v2-migration.md)\n\n## Version Notes\n\n### 2.2.0\n\nVersion 2.2.0 adapts the scheduled runtime to OpenClaw command automations and archived-session enforcement. `companion-presence` now runs `companion_presence_tick.py` as an exact argv Gateway command instead of starting a full isolated model turn whose only job was to execute the wrapper and reply `NO_REPLY`. Each matched event uses a fresh dispatch-scoped companion session so OpenClaw never has to resume an archived long-lived runtime session. If the external CLI cannot resolve the configured WeChat plugin channel, the fixed send entrypoint uses its existing explicit-contract direct fallback and keeps a failed delivery retryable. The model-backed `companion-build-day-schedule` job keeps its generation workflow but enables lightweight bootstrap context because its payload already lists the required inputs.\n\n中文说明：2.2.0 把 `companion-presence` 改成 OpenClaw 原生 command automation，用精确 argv 直接运行 wrapper，不再每 15 分钟先启动一次 Codex turn。命中事件后为本轮派生新的 dispatch session，不再复用可能已归档的长期 session；如果外部 CLI 也找不到自定义微信渠道，固定发送入口会按同一 delivery contract 使用既有直连兜底，并把失败发送保留为可重试。这样可以同时避开 `turn-accepted` 卡住、归档会话拒绝启动和空转模型上下文；每日 日程 builder 仍使用模型，但启用轻量上下文，只加载任务明确要求的项目输入。\n\n### 2.1.9\n\nVersion 2.1.9 makes text delivery use the same deterministic wrapper boundary as media delivery. The stable companion session must not call the runtime `message(action=\"send\")` tool for presence text. It writes the story and calls `companion_presence_tick.py --send-story --story-stdin`; that helper loads the saved contract from `presence-dispatch.json`, sends through `openclaw message send --channel/--target/--account --message`, and then runs `state_commit.command`. Media events start async media generation only after `--send-story` succeeds.\n\n中文说明：2.1.9 把正文投递也收回到固定脚本入口，不再依赖 Codex runtime 的 message 工具解析自定义渠道。稳定 session 只负责写正文和调用 `--send-story --story-stdin`；脚本按 dispatch lock 里的 `delivery_contract` 显式发文本，成功后再提交状态。这样新用户和老用户都统一走外部 OpenClaw CLI 的真实渠道表，避免 `Unknown channel` 或 current chat 误路由。\n\n### 2.1.8\n\nVersion 2.1.8 makes media delivery independent of model follow-up behavior. For media events, `companion_presence_tick.py` now starts a background recent-media watcher immediately after the stable companion session is launched; the watcher finds the next media task created by that stable session, waits for completion, extracts the generated media path, and sends it with the explicit `delivery_contract`. `--watch-media-task` remains the task-id-specific helper, and `--send-media` remains the direct send fallback.\n\n中文说明：2.1.8 不再要求模型在媒体工具返回后“自觉”运行 watcher。wrapper 会自己启动后台 watcher，按稳定 session 和启动时间找到本次生成任务，然后用 `delivery_contract` 显式投递到真实渠道，避开 Codex runtime 的 `internal-ui/current chat` 误路由。\n\n### 2.1.7\n\nVersion 2.1.7 adds deterministic media delivery entrypoints for async OpenClaw media tasks. After the main presence turn starts media generation, it runs `companion_presence_tick.py --watch-media-task` with the returned task id; the helper waits for the generated media path and sends through the explicit `delivery_contract` using `openclaw message send --channel/--target/--account/--media`. `--send-media` remains the direct completion fallback and retries once if a result indicates `internal-ui` or current-webchat routing.\n\n中文说明：2.1.7 为异步媒体任务增加固定监控和投递入口。主 turn 启动媒体生成后立刻运行 `--watch-media-task` 等待任务完成并按 `delivery_contract` 显式发送到真实渠道；不再依赖 completion turn 自己理解 current chat。\n\n### 2.1.6\n\nVersion 2.1.6 hardens OpenClaw CLI child processes launched by the presence wrapper so cron-inherited CA settings cannot reintroduce Keychain startup failures. It also keeps dispatch-lock startup acknowledgement and launch-error diagnostics in the release contract.\n\n中文说明：2.1.6 加固了 presence wrapper 启动 OpenClaw CLI 子进程时的 CA 环境，避免 cron 继承的系统 CA 设置再次触发 Keychain 启动失败；同时保留稳定 session 启动确认和启动失败诊断能力。\n\n### 2.1.5\n\nVersion 2.1.5 removes prompt wording that explicitly names concrete runtime tools while preserving the mandatory real web-search requirements for day-schedule generation and matched-event presence writing.\n\n### 2.1.4\n\nVersion 2.1.4 removes the legacy visible `mode` field from the presence prepare contract and default wrapper command. There is now only one public presence flow: cron calls the deterministic wrapper, the wrapper prepares the current event, and matched events are handed to the stable companion session.\n\n### 2.1.3\n\nVersion 2.1.3 moves the cron-side current-event decision into `scripts/companion_presence_tick.py`. The visible cron should run in an isolated session and only call that wrapper; the wrapper performs fresh prepare deterministically, then starts the stable companion session only for matched events. Presence writing must run a small public web search for the matched event, and media events now commit state after visible text delivery so media failure does not block later events.\n\n### 2.1.2\n\nVersion 2.1.2 returned media delivery to the native OpenClaw completion flow by running `companion-presence` in a stable companion session. Text sends first, media generation runs asynchronously, and the completion turn in the same companion session sends media.\n\n### 2.1.1\n\nVersion 2.1.1 changes `companion-presence` to a stateless single-turn runtime task. Continuity remains in local state files, and event media callbacks must use a self-contained payload instead of relying on a long-lived companion session history.\n\n### 2.1.0\n\nVersion 2.1.0 is the public release cleanup for the simplified presence companion. It removes git-local packaging assumptions, keeps ClawHub ignore rules authoritative, preserves the generic internet-search day-schedule rule, and checks that local maintenance Markdown does not enter the publishable surface.\n\n### 2.0.4\n\nVersion 2.0.4 hardens the generic day-schedule templates. It preserves the mandatory internet-search material rule without local profile examples, removes stale upgrade links, cleans finished schedule examples so they do not contain generation constraints, and clarifies first-time setup plus old-install migration.\n\n### 2.0.3\n\nVersion 2.0.3 improves the published skill surface. It rewrites the main skill entry as a product-first quick-start page, removes an unreferenced optional source note from the package, and keeps the release smoke fixture aligned with the current runtime state schema.\n\n### 2.0.2\n\nVersion 2.0.2 finishes the release-hardening pass for publishing. It keeps local runtime state out of the ClawHub package, documents the OpenClaw async media callback flow, and validates the publishable release surface before upload.\n\n### 2.0.1\n\nVersion 2.0.1 hardened the 2.0 release surface, removed tests from the ClawHub package, tightened week/day generation quality, and added event-level media instructions through the OpenClaw async media callback flow.\n\n### 2.0.0\n\nVersion 2.0.0 made the presence runner the only default active path by merging `scripts/companion_ping.py` into `scripts/companion_run.py` and removing the old render/full path from the default release surface.\n\n## Maintainer Release Gate\n\nBefore publishing a new version, run:\n\n```bash\npython3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG>\n```\n\nThe validator compiles scripts, validates JSON and Markdown assets, runs the presence dry-run flow, checks the runner contract, and scans the release surface for private channel identifiers and obsolete cron contract terms.\n\nFile v2.2.0:_meta.json\n\n{\n  \"ownerId\": \"kn71yvy6nsxy27kp8krr9879ys80gv9h\",\n  \"slug\": \"cyber-girlfriend\",\n  \"version\": \"2.2.0\",\n  \"publishedAt\": 1788403624689\n}\n\nFile v2.2.0:references/agent-first-time-qa-template.md\n\n# Agent First-Time Q&A Template\n\nUse this only when an agent needs literal onboarding wording for a brand-new setup conversation.\n\nThis file is a conversation aid, not the source of truth.\nFor setup order and defaults, read [first-time-setup.md](./first-time-setup.md).\nFor field ownership, read [configuration.md](./configuration.md).\nFor presence cron wiring, read [presence-integration.md](./presence-integration.md).\n\n## Core Rule\n\nDo not ask the user to write prompts during first setup.\n\nThe agent should:\n- collect routing and pacing decisions\n- collect enough real-world persona anchors when private life is enabled\n- ask whether any fixed life anchors should always enter the day\n- translate the result into config, Markdown life files, and cron jobs\n\n## Recommended Opening\n\nUse wording like:\n\n> 我先按首配流程帮你收最小必要信息，不让你自己写 prompt。先确认投递目标，再写她的人设和生活锚点，最后我来落配置、日程和 presence cron。\n\n## Default Question Order\n\n### 1. Delivery route\n\n> 陪伴消息准备发到哪个渠道？把目标 id 一起给我。  \n> 如果这个渠道发消息还要指定发送账号，也一起给我。\n\nCapture:\n- `delivery.channel`\n- `delivery.owner_target`\n- `delivery.account` when needed\n\n### 2. Owner profile source\n\n> owner 信息你要我从 OpenClaw 的 USER.md 导入，还是你手动给我一份简短自定义？  \n> 我只需要区分 owner 和 companion，不会把渠道账号或 session id 塞进 prompt。\n\nCapture:\n- `owner_profile.source`: `user_md`, `manual`, or `none`\n- stable owner identity fields only\n\n### 3. Character profile reality anchors\n\n> 如果你要她有活人感，我还需要她现实里的身份信息：年龄或阶段、学生/上班/创作者是哪种、城市、平时更常看什么内容。\n\nCapture when available:\n- 基础身份\n- 兴趣与内容偏好\n- 关系表达和禁区边界\n\n### 4. Required life events\n\n> 有没有什么你希望她每天一定会经历、但不一定每次都发消息的事？比如通勤、晚课、健身、夜里收尾、固定拍照散步。  \n> 有的话我会写成 `必定发生：是` 的日程锚点。\n\nFor each anchor, ask only:\n- 时间或时间窗口\n- 持续多久\n- 她在哪里\n- 她正在做什么\n- 可以自然提到什么\n- 可以怎么轻轻接 owner\n- 不要写成什么\n\n### 5. Quiet hours\n\n> 安静时段你想设几点到几点？没要求我就用 01:00 到 08:00。\n\n### 6. Confirmation before writing\n\n> 执行前我先确认范围：会写 `config.local.json`、`character-profile.md`、`day-schedule.md` 和运行状态文件；创建或更新 `companion-build-day-schedule`、`companion-presence` 两个定时任务，并把任务名、时间和 payload 类型列给你。投递渠道、账号和目标只显示脱敏值。  \n> 日程生成和命中事件写作会访问公开网页；验收会向刚确认的 owner 目标发送一条可见测试消息。需要暂停时只禁用这两个任务并保留配置和状态；永久删除任务会另行征得你的明确同意。  \n> 请确认后我再执行这些写入、联网搜索、任务变更和一次真实发送。\n\n## Fast One-Shot Version\n\nWhen the user wants the shortest possible onboarding, ask this bundle:\n\n> 我按最快首配来收信息，你回我这些就行：  \n> 1. 发到哪个渠道，目标 id 是什么，需不需要指定发送账号  \n> 2. 她现实里是什么身份：年龄/阶段、学校或工作、城市、常看的内容方向  \n> 3. 有没有必定发生的日常锚点：时间、持续多久、场景、正在做什么  \n> 4. 安静时段，如果没要求我就用默认值  \n> 5. 我会先预览要写的文件、两个任务的名称/时间/payload、脱敏投递目标、公开搜索和一次测试发送；你明确确认后我才执行。暂停只禁用任务并保留配置/状态，永久删除需另行确认。\n\n## Ownership Reminder\n\nMap answers like this:\n\n| Answer type | Goes to config | Goes to Markdown / cron |\n| --- | --- | --- |\n| delivery route | yes | used by presence send step |\n| owner identity boundary | yes | used to separate owner from companion |\n| character profile reality anchors | `character_profile_path` | `character-profile.md` |\n| quiet hours | yes | validators enforce schedule windows |\n| required life anchors | yes | `day-schedule.md` as `必定发生：是` |\n| presence cadence | no | OpenClaw cron |\n\n## Do Not Do These\n\nDo not:\n- ask the user to handwrite prompt prose\n- dump every schema field at once\n- ask for derived_profile values up front\n- assume the current chat session is the proactive delivery target\n- recreate old four-slot visible cron jobs unless the user explicitly asks\n\nIf the delivery route or first verification target is still fuzzy, onboarding is not complete.\n\nFile v2.2.0:references/configuration.md\n\n# Configuration\n\nKeep the runtime file name as `config.local.json` for compatibility.\nDo **not** rename it for v2. Upgrade by adding `\"version\": 2` and the new sections.\n\n## Design Goal\n\nAsk the user for as little as possible.\n\nSplit the config into three kinds of fields:\n1. **Companion character profile pointer** — where the Markdown character profile lives\n2. **Owner identity boundary** — who the owner is, kept separate from companion life\n3. **Agent-generated life model** — derived rhythm, current day schedule, continuity state\n\nThe user should mostly fill the first two kinds. The agent/runtime should generate the third kind.\n\n## Field Ownership Rule\n\nFor first-time setup, keep this split strict:\n\n- `config.local.json` stores profile paths, delivery, pacing policy, runtime paths, and optional long-lived source toggles\n- `character-profile.md` stores the companion's core identity, tone, relationship expression, interests, and lived anchors\n- `life_schedule.day_schedule.required_events` stores stable user-defined life anchors\n- presence automation payloads store only an exact wrapper argv, not prompt prose\n- generated state files store derived rhythm, continuity, and current day schedules\n\nDo not turn `config.local.json` into a dump of prompt prose.\n\n## Required Sections\n\n### `version`\n\n- Set to `2`\n\n### `character_profile_path`\n\nPath to the companion's core Markdown character profile.\n\nRecommended:\n- `./state/character-profile.md`\n\nThe published example is:\n- `assets/character-profile.example.md`\n\nThis file now owns the full companion persona:\n- name and owner-facing nickname\n- age / life stage\n- identity role\n- city / district\n- school, work, or creative background\n- personality, interests, entertainment tastes, expression style, relationship style, and safety boundaries\n\n### `persona` (deprecated)\n\nDeprecated compatibility cache. New installs should not ask users to maintain this JSON object.\n\nIf an older `config.local.json` still has `persona`, it may be used as a migration source or fallback.\nThe forward direction is to migrate those fields into `character-profile.md` and keep config JSON focused on machine-readable runtime settings.\n\n### `owner_profile`\n\nOptional lightweight owner identity boundary. Its job is not to over-control style; its job is to stop the companion's school, work, friends, dorm, class, or other private-life material from being projected onto the owner.\n\nRecommended fields:\n- `source` — `manual | user_md | none`\n- `user_md_path` — optional path when importing from OpenClaw `USER.md`\n- `name`\n- `preferred_name`\n- `pronouns`\n- `location`\n- `timezone`\n- `identity_summary`\n- `not_assumptions` — optional user-defined taboos or identity assumptions to avoid\n\nFor first-time setup or upgrade, ask one product question:\n`owner 信息要从 USER.md 导入，还是你手动自定义？`\n\nIf the user picks `USER.md`, import only stable identity fields such as name, preferred name, pronouns, location, and timezone. Do not copy messaging-platform session keys, account IDs, direct-chat IDs, or routing rules into prompt-facing outputs.\n\nDo not add a `communication_style` field by default. The agent should infer communication from `character-profile.md`, relationship guardrails, and the owner boundary.\n\n### `relationship`\n\nCompanion relationship guardrails.\n\nRecommended fields:\n- `mode`\n- `intimacy_baseline`\n- `jealousy_allowed`\n- `clinginess_ceiling`\n- `conflict_style`\n\n### `delivery`\n\nOutbound target configuration.\n\nRequired fields:\n- `channel`\n- `owner_target`\n\nOptional:\n- `account`\n- `owner_session_key` as a deprecated compatibility value for older native heartbeat installs\n\nFirst-time setup rule:\n- ask for the real DM target id, not the current control UI label\n- if the selected channel requires a sender account, capture it now instead of leaving it implicit\n\n### `timezone`\n\nUse the owner's real timezone / the companion's lived timezone.\nThis is required because the private-life layer should track real-world dates,\nholidays, and time-of-day rhythms.\n\n### `schedule`\n\nKeep only pacing policy here.\n\nRequired fields:\n- `quiet_hours_start`\n- `quiet_hours_end`\n\nDo **not** store her personal life schedule here.\nPresence cron cadence belongs to OpenClaw cron configuration. Her lived day belongs to `day-schedule.md`.\n\nUse this section only for:\n- quiet hours\n\nDo not try to encode fixed time-slot task text here.\n\n旧安装里如果仍有 `cooldown_sec`，迁移后应删除。Heartbeat pacing is now\ndriven by the current `day-schedule.md` event plus pending-delivery and\nsame-event duplicate guards.\n\n### `behavior`\n\nRuntime behavior fields.\n\nRecommended fields:\n- `emotion_thresholds.present_sec`\n- `emotion_thresholds.slightly_needy_sec`\n- `emotion_thresholds.misses_him_sec`\n\nDefault values when the user has no preference:\n- `present_sec`: `7200`\n- `slightly_needy_sec`: `10800`\n- `misses_him_sec`: `14400`\n\nOptional but recommended:\n- `derived_profile`\n  - `activity_level`\n  - `social_energy`\n  - `sleep_profile`\n  - `weekend_outdoor_bias`\n  - `expression_density`\n\n`derived_profile` should be generated by the agent from `character-profile.md`,\nnot manually filled by the user unless they want an override.\n\nFor new users, prefer generating `derived_profile` automatically after the character profile is captured. Do not block first setup on these knobs.\n\n### `life_schedule`\n\nThis is the new private-life layer.\nIt should drive the companion's own lived context.\n\nRecommended fields:\n- `enabled`\n- `day_schedule`\n  - `enabled`\n  - `schedule_path`\n  - `refresh_mode`\n  - `midday_refresh`\n  - `required_events`\n- `continuity`\n  - `enabled`\n  - `life_log_path`\n\n`required_events` holds user-defined life anchors that should appear in the daily schedule as `必定发生：是`. These anchors are not render spec fields and do not guarantee that a message is sent.\n\nOptional `media_hint` can be used when a required event should create media. The daily schedule builder should turn `media_hint` into the event's `媒体信息` field. For example, a 19:30 required event can ask to share one life photo whose concrete scene is derived from that day's main schedule rather than fixed in config.\n\n### `runtime`\n\nExternalize runtime hooks here.\n\nSuggested fields:\n- `workspace_root`\n- `sessions_store_path`\n- `state_file`\n- `healthcheck_command`\n- `cron_jobs_file`\n- `jobs_list_command`\n\n`sessions_store_path` is used for runtime state inspection and compatibility. On OpenClaw versions with command automations, current `companion-presence` uses an isolated exact argv command payload to call `companion_presence_tick.py`; the wrapper derives a fresh dispatch-scoped companion session only after fresh prepare matches an event. Media events start OpenClaw async generation, and the wrapper also starts `companion_presence_tick.py --watch-recent-media-task` so generated media is sent through the explicit `delivery_contract` after the media task succeeds. Event state is committed after the text presence story is visibly sent, not after media success.\n\n中文说明：异步媒体补发由 wrapper 后台 `--watch-recent-media-task` 自动等待任务完成并显式发送；原生 completion 不再是正确渠道投递的主路径，避免 runtime 把 current chat 误解为 Codex `internal-ui`。\n\n\n### `sources`\n\nOptional reality-sync sources.\n\nSuggested blocks:\n- `calendar_context`\n- `weather_context`\n\n## State Files\n\nRecommended private-life files:\n- `companion-state.json` — pacing + relationship state\n- `day-schedule.md` — today's concrete event schedule\n- `life-log.jsonl` — continuity claims already used in sent messages\n- task-specific source files only when a concrete cron really needs them\n\n## Upgrade Rule\n\nFor old installs:\n- keep the same `config.local.json` path\n- add `\"version\": 2`\n- preserve existing `delivery` / `schedule` / `runtime`\n- append `relationship`, `behavior.emotion_thresholds`, and `life_schedule`\n- let the agent populate `behavior.derived_profile` automatically\n\nFor persona migration, use `migrate_config.py`. It reads the deprecated `persona`\nblock, writes `character-profile.md` when the profile does not already exist,\nsets `character_profile_path`, and validates the generated profile:\n\n```bash\npython3 scripts/migrate_config.py --config config.local.json --write\n```\n\nUse `--overwrite-character-profile` only when the user explicitly wants to replace an existing profile.\n\nWithout `--write`, the command only prints the migration summary and does not create the profile. Character-profile validation runs after writing unless `--skip-character-profile-validation` is passed.\n\nUse [standard-init-upgrade-flow.md](./standard-init-upgrade-flow.md) for the full upgrade checklist, including cron payload updates and real delivery verification.\n\n## Validation Rule\n\nThe schema should validate a fully materialized v2 config, but the onboarding\nagent may still bootstrap missing generated fields before first real use.\n\nBefore calling setup complete, the generated local config must be materialized:\n- no placeholder strings such as `<REQUIRED_CHANNEL>` or `<RUNTIME_SPECIFIC>`\n- all runtime paths point at the actual machine layout\n- `runtime.state_file` parent exists or can be created\n- `life_schedule` paths point at the intended state directory when enabled\n- `delivery.owner_target` and sender account match the real outbound channel\n\n## First-Time Setup Reminder\n\nFor a fresh install, pair this file with [first-time-setup.md](./first-time-setup.md):\n\n- this file defines where fields belong\n- `first-time-setup.md` defines what to ask, what to default, and what should stay in the presence automation instead of config\n\nFile v2.2.0:references/contract-schema.md\n\n# Turn Contract\n\n`scripts/companion_presence_tick.py` is the default presence automation entrypoint. New OpenClaw installations invoke it directly with an exact argv command payload; the wrapper runs `companion_run.py` for fresh prepare, exits quietly on skip, and starts a fresh dispatch-scoped companion session only when a current event is matched.\n\n## Prepare Stage\n\nCommand:\n\n```bash\npython3 <SKILL_DIR>/scripts/companion_run.py --stage prepare --config <CONFIG> --no-record-pending\n```\n\nAutomation command payload:\n\n```json\n{\n  \"kind\": \"command\",\n  \"argv\": [\n    \"python3\",\n    \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n    \"--config\",\n    \"<CONFIG>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n  \"outputMaxBytes\": 65536\n}\n```\n\nPrepare selects the current `day-schedule.md` event by real local time. Events marked `必定发生：是` are life facts, not guaranteed sends.\n\nRequired output fields:\n- `status`\n- `run_id`\n- `life_context`\n- `delivery_contract`\n- `media_contract`\n- `state_commit`\n- `next_step`\n\n`life_context` is structured and must contain:\n- `generated_at`\n- `timezone`\n- `speaker`\n- `today`\n- `event`\n- `reality_check`\n\nOptional:\n- `delivery_mood`\n\nThe tick wrapper passes the prepared contract to a fresh dispatch-scoped companion session when status is ok. That session writes one first-person companion presence story directly from `life_context`. There is no separate render stage and no `render_spec` in the current architecture. A prepared run id is appended to the configured base session key, so an archived session from an earlier event is never resumed.\n\nPrepare output must not include duplicated task fields or local-only execution hints:\n- no top-level `primary_goal`\n- no `render_spec`\n- no local runbook fields\n- no private local paths or channel identifiers beyond the configured delivery contract\n\nAgents should not infer delivery ownership from prose. Follow these fields:\n- `delivery_contract.send_in_main_turn = true`: text may be sent in the main turn.\n- `media_contract.kind = event_media`: the current event requires media.\n- `media_contract.async = true`: OpenClaw media generation is asynchronous.\n- `media_contract.tool_name`: the runtime-selected async media generator for the matched media type.\n- `media_contract.completion_event_is_sender = true`: the media task is asynchronous and will produce a native OpenClaw completion event.\n- `media_contract.callback_context.strategy = same_stable_session`: \"stable\" means the same dispatch-scoped session for the lifetime of this one media task, not a session reused across events.\n- `media_contract.callback_context.requires_original_session_context = true`: the completion turn relies on that dispatch session context to keep the original delivery contract available.\n- `state_commit.when`: defines when pacing state can be committed.\n\nThe presence agent must send text through `companion_presence_tick.py --send-story --story-stdin`, not through the runtime `message(action=\"send\")` tool. `--send-story` reloads the saved contract from the dispatch lock, sends the text presence story with the explicit `delivery_contract`, and runs `state_commit.command` only after visible text delivery succeeds.\n\n`life_context.event.media_info` may describe a concrete photo, audio, video, or similar media artifact for the matched event. If present, the presence agent calls the selected async media generation path only after `--send-story` succeeds. The wrapper also starts `companion_presence_tick.py --watch-recent-media-task` for the dispatch session; that helper finds the next media task created after launch, waits for generated media paths, and sends with the explicit `delivery_contract`. If the native media completion later returns to the same dispatch session, it may only use `companion_presence_tick.py --send-media` as a fallback and must not run `state_commit.command` again.\n\n中文说明：正文主路径是固定 `--send-story` 入口，媒体补发主路径是 wrapper 自动启动的 `--watch-recent-media-task`。两者都不能让模型自己判断 current/original chat；completion 兜底也只能调用固定 `--send-media` 入口。\n\nFor both media events and text-only events, `state_commit.when = after_text_send`. Media success is a follow-up enhancement, not the event completion gate.\n\n`media_contract` stays intentionally generic. It must not carry local runbook paths, private workspace details, or user-specific media instructions.\n\nUse `assets/turn-contract.schema.json` for machine validation.\n\nFile v2.2.0:references/first-time-setup.md\n\n# First-Time Setup Guide\n\nUse this when a user is configuring the skill for the first time or rebuilding it from scratch.\n\nGoal:\n- collect only the minimum decisions the user must make\n- keep delivery fields correct\n- generate `character-profile.md` and `day-schedule.md`\n- wire `companion-presence` without turning onboarding into prompt-writing\n- treat user-defined fixed content as life anchors, not guaranteed sends\n\nIf you need literal onboarding wording, use [agent-first-time-qa-template.md](./agent-first-time-qa-template.md).\n\n## First-Time Setup Order\n\nFollow this order. Do not skip ahead to polishing wording.\n\n1. Confirm the proactive delivery destination.\n2. Decide whether owner info should be imported from `USER.md`, customized manually, or skipped.\n3. Capture the companion's character-profile inputs.\n4. Ask whether the user wants any `必定发生：是` life anchors.\n5. Preview the exact files, masked delivery route, public-search use, cron names/schedules/payload types, and one controlled real send; wait for explicit user confirmation.\n6. Write `character-profile.md` and materialize `config.local.json`.\n7. Generate the first `day-schedule.md` with 3-5 ordinary events plus configured required events.\n8. Create or update `companion-build-day-schedule` and `companion-presence`.\n9. Run validation and one controlled user-visible verification.\n\n## What The User Must Decide\n\nAsk for or infer only these fields first:\n\n| Question | Destination | Required? | Notes |\n| --- | --- | --- | --- |\n| Which channel should proactive messages use? | `delivery.channel` | yes | Example: direct message or another configured OpenClaw channel |\n| What exact recipient id should delivery use? | `delivery.owner_target` | yes | Use the real target id, not the current UI label |\n| Which sending account should be used? | `delivery.account` | channel-dependent | Required on channels that need a specific sender account |\n| Import owner info from `USER.md` or customize manually? | `owner_profile` | recommended | Import only stable identity fields; never prompt with private channel/account ids |\n| How old or what life stage is she? | `character-profile.md` | strongly recommended | Example: freshman, early-career, creator |\n| What exactly is her real-world role? | `character-profile.md` | strongly recommended | Example: design intern, game content creator |\n| Which city or district does she live around? | `character-profile.md` | strongly recommended | City alone is often too coarse for believable planning |\n| What interests and entertainment does she naturally follow? | `character-profile.md` | strongly recommended | Used for daily reality anchors and local life texture |\n| Any fixed things that should always enter her day? | `life_schedule.day_schedule.required_events` | optional | These become `必定发生：是` events, not guaranteed messages |\n| Quiet hours? | `schedule.quiet_hours_start` / `schedule.quiet_hours_end` | yes | Default is fine when the user has no preference |\n\nDo not ask for advanced style tuning, derived profile fields, low-level life-schedule internals, or legacy visible cron modes during the first pass.\n\n## Owner Profile Rule\n\nKeep owner information light. The setup only needs enough to distinguish owner from companion:\n- `source`: `user_md`, `manual`, or `none`\n- `preferred_name`\n- `pronouns`\n- `location`\n- `timezone`\n- optional `identity_summary`\n- optional `not_assumptions`\n\nWhen `USER.md` exists, offer import as the recommended path. Import only stable identity fields and do not copy routing identifiers, direct-chat IDs, account IDs, or session keys into prompt-facing context.\n\n## Character Profile Detail Rule\n\nIf `life_schedule.enabled` is true or the user explicitly wants stronger 活人感, do not stop at a thin persona like \"college student in a city\".\n\nAt minimum, capture:\n- life stage or age band\n- concrete school, work, creator, or daily identity\n- city plus a more local area when known\n- interests plus the kinds of entertainment and content she actually follows\n\nWrite these into `character-profile.md`, not into `config.local.json -> persona`.\n\n## Required Event Anchors\n\nRequired events are life facts. They make the daily schedule include something the user cares about, but they do not force a message to be sent.\n\nFor each anchor, capture:\n- time window or preferred time\n- title\n- duration\n- scene\n- what she is doing\n- what can be naturally mentioned\n- owner interaction entry\n- what not to write it as\n\nStore anchors in `life_schedule.day_schedule.required_events`. The daily builder turns them into `day-schedule.md` events with `必定发生：是`.\n\n## Recommended Starter Defaults\n\nUse these when the user says \"先给我一套能跑的\" or has no strong preference:\n\n- daily schedule builder: `10 7 * * *`, isolated model turn, `lightContext: true`, no delivery\n- presence cron: every 15 minutes, isolated exact argv command payload that calls `companion_presence_tick.py`; the wrapper starts a fresh dispatch-scoped companion session only after a matched event\n- quiet hours: `01:00` to `08:00`\n- required events: none unless the user names one\n\nThe old four-slot content cron setup is deprecated. If the user asks for a fixed daily habit, model it as a required event anchor first.\n\n## Authorization Preview Gate\n\nBefore the first mutation, show the user one compact preview containing:\n\n- the exact local files that will be created or changed\n- both recurring job names, schedules, and payload types\n- the delivery channel/account/target with sensitive identifiers masked\n- that daily schedule generation and matched-event writing use public-web search\n- that verification includes one visible message to the confirmed owner target\n- that pausing disables the exact jobs and preserves config/state, while permanent removal requires a separate explicit request\n\nWait for an explicit confirmation such as “确认执行” before writing files, searching the public web, creating/updating/enabling jobs, or sending the controlled verification message. The confirmation may authorize the whole previewed setup; do not silently expand beyond that scope.\n\n## Materialized Config Gate\n\nAfter writing `config.local.json`, verify it is not just a copied example.\n\nRequired before cron creation:\n- no placeholder values like `<REQUIRED_CHANNEL>` or `<RUNTIME_SPECIFIC>`\n- actual `character_profile_path`\n- actual `runtime.workspace_root`\n- actual `runtime.sessions_store_path`\n- actual `runtime.state_file`\n- actual `runtime.healthcheck_command`\n- actual `life_schedule` state paths when enabled\n- default `behavior.emotion_thresholds` if the user did not customize them\n\nFor rebuilds or scripted setup, use the migration helper to materialize defaults:\n\n```bash\npython3 scripts/migrate_config.py --config <CONFIG> --owner-source user_md --write\n```\n\nUse `--owner-source manual` or `--owner-source none` when the user chooses those paths.\n\n## Markdown Life Text Initialization\n\nWhen private life is enabled, initialize the Markdown files before creating presence cron. Do not leave the first real run to invent life context from an empty state directory.\n\nCreate or refresh these files in order:\n\n1. `character-profile.md`\n2. `day-schedule.md`\n\nFor `day-schedule.md`, derive today's 3-5 concrete ordinary events directly from the character profile, today's date, public search materials, recent life log, and configured required events. Before ordinary events are written, explicitly run web search with the standard 4-5 keyword mix: one city/weather keyword, one local area/school/workplace/community keyword, one identity/occupation keyword, and one or two interest keywords. Add required events into the same `## 4. 日程事件` section as `必定发生：是`.\n\nEach event must have:\n- `HH:mm - 事件标题`\n- `必定发生：是/否`\n- `执行时间`\n- scene, action, mood, natural mention, interaction entry, media info, and avoid rule\n- no quiet-hour overlap\n- no overlapping event windows\n- no duplicate event types in the same day, including duplicates with configured required events\n- every searched category is consumed by at least one normal character behavior event rather than becoming a standalone news/material-browsing event\n\nRun the Markdown validators immediately:\n\n```bash\npython3 scripts/validate_character_profile.py --profile <CHARACTER_PROFILE>\npython3 scripts/validate_day_schedule.py --config <CONFIG> --path <DAY_SCHEDULE>\n```\n\nIf validation fails, fix the Markdown file itself before creating cron jobs.\n\n## Delivery Pitfalls To Prevent\n\nBefore creating cron jobs, make sure the onboarding agent has stated these rules:\n\n- proactive outbound delivery follows the local config delivery block\n- `companion-presence` runs as an isolated exact argv command automation, not the owner conversation and not an empty model turn\n- the command uses fixed installer-authored argv, bounded timeout/output, and no shell interpolation\n- `companion_presence_tick.py` starts a fresh dispatch-scoped companion session only after fresh prepare returns a matched event\n- final companion text is sent through the prepared delivery contract\n- state is committed only after visible text delivery succeeds\n- media turns commit state after visible text delivery, then start OpenClaw async media generation; the native completion returns to the same dispatch session and only sends media\n- required events are not guaranteed sends\n\n## First Verification Gate\n\nFor a fresh install, do one controlled real run after setup and after the authorization preview was explicitly confirmed:\n\n1. Run `python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG> --skip-smoke`.\n2. Ensure `day-schedule.md` has a current event or create a temporary validated test schedule.\n3. Run `companion_run.py --stage prepare --config <CONFIG> --no-record-pending`.\n4. Confirm the prepare contract has `life_context`, `delivery_contract`, `media_contract`, and `state_commit`; it must not have `render_spec`, `media_task_record_contract`, or top-level `primary_goal`.\n5. Send one first-person companion presence story to the intended owner target through `companion_presence_tick.py --send-story`.\n6. Confirm `--send-story` commits state only after text delivery succeeds. For media events, start async media after the commit; do not wait for media success to mark the event complete.\n\nIf delivery fails, fix routing first. Do not keep polishing personality copy while the send path is still untrusted.\n\nFile v2.2.0:references/presence-integration.md\n\n# Presence Integration\n\nUse this when wiring the skill into OpenClaw jobs or sessions.\n\n当前默认主动链路只有 `companion-presence`。支持 command automation 的 OpenClaw 运行时使用 exact argv payload 直接调用 `scripts/companion_presence_tick.py`，不先启动只负责执行 wrapper 的模型 turn；该 wrapper 再确定性运行 `scripts/companion_run.py --stage prepare --no-record-pending`，读取当前 `day-schedule.md` 事件。未命中时静默退出，命中后才按 run id 派生新的 dispatch session 并发送 presence story；如果是媒体事件，wrapper 还会启动后台 recent-media watcher 来按显式合同投递生成媒体。OpenClaw completion 可能仍回到同一个 dispatch session，但不再是正确渠道投递主路径。事实连续性仍只来自本地状态文件，不复用可能已归档的旧 session。\n\n## Runtime Pieces\n\nRequired local pieces:\n- `scripts/companion_run.py`\n- materialized `config.local.json`\n- `state/character-profile.md`\n- `state/day-schedule.md`\n- `state/companion-state.json`\n- optional continuity file `state/life-log.jsonl`\n\n`config.local.json` may contain real local paths and delivery ids. Do not copy those values into publishable docs, examples, cron templates, or user-visible companion text.\n\n## Presence Cron Shape\n\nRecommended job:\n\n```json\n{\n  \"name\": \"companion-presence\",\n  \"description\": \"Owner-only cyber girlfriend presence cron\",\n  \"schedule\": {\n    \"kind\": \"cron\",\n    \"expr\": \"0 * * * *\",\n    \"tz\": \"Asia/Shanghai\"\n  },\n  \"sessionTarget\": \"isolated\",\n  \"payload\": {\n    \"kind\": \"command\",\n    \"argv\": [\n      \"python3\",\n      \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n      \"--config\",\n      \"<CONFIG_PATH>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n    \"outputMaxBytes\": 65536\n  },\n  \"delivery\": {\n    \"mode\": \"none\"\n  },\n  \"enabled\": true\n}\n```\n\nUse exact argv rather than a shell string. The command payload is an operator-authored Gateway execution surface, so every executable and argument must be fixed by the installer; do not interpolate owner text, event text, config fields, or model output into the command.\n\nCLI shape:\n\n```bash\nopenclaw cron edit <JOB_ID> \\\n  --command-argv '[\"python3\",\"<SKILL_DIR>/scripts/companion_presence_tick.py\",\"--config\",\"<CONFIG_PATH>\"]' \\\n  --command-cwd '<SKILL_DIR>' \\\n  --command-env 'PYTHONUNBUFFERED=1' \\\n  --timeout-seconds 120 \\\n  --output-max-bytes 65536 \\\n  --session isolated \\\n  --no-deliver\n```\n\nOpenClaw 2026.8.1 的 `cron edit` 在 agent payload 转 command payload 时，省略 `--command-env` 可能把空 env 送入校验并报 `command env must be an object`。固定的 `PYTHONUNBUFFERED=1` 既规避该转换问题，也让 wrapper 输出及时进入任务日志；不要用该参数注入动态内容或秘密。\n\nOlder OpenClaw versions without command payloads may keep the legacy isolated `agentTurn` fallback. That compatibility payload must enable lightweight context, run only `companion_presence_tick.py --config <CONFIG>`, and reply `NO_REPLY` for every handled wrapper status. It is not the default for new or upgraded installations.\n\nDo not pass `--event-time` in the live cron. Presence reads the real current local time.\n\n## Authorization And Lifecycle Controls\n\nBefore creating, editing, or enabling either recurring job, read the current job definitions and preview the exact job ids/names, schedules, payload types, fixed argv, masked route, public-search use, and controlled verification send. Wait for explicit user confirmation before applying the preview or sending the test message.\n\nPause is reversible and must not delete local files:\n\n```bash\nopenclaw cron disable <PRESENCE_JOB_ID>\nopenclaw cron disable <BUILDER_JOB_ID>\n```\n\nResume only the exact jobs the user wants:\n\n```bash\nopenclaw cron enable <BUILDER_JOB_ID>\nopenclaw cron enable <PRESENCE_JOB_ID>\n```\n\nKeep the previous job definitions until the edited jobs have passed validation so their payloads can be restored if needed. Use `openclaw cron rm <JOB_ID>` only after the user separately and explicitly requests permanent removal and the exact ids have been resolved. Pausing or rolling back jobs must not delete `config.local.json`, character/day Markdown, state, or continuity logs.\n\n## Message Rules\n\n- Final text must be first person from the companion's perspective and must fit the cyber-girlfriend persona.\n- Unless the matched required event defines a special structure, write one complete, rich, specific event story.\n- Include the companion's current emotion and inner thought.\n- Write at least 160 Chinese characters; before sending, self-check the final text and expand with event details or inner thought if it is shorter.\n- If the current event contains an interaction entry for the user, express it naturally and do not omit it.\n- Use the current event in `life_context`, not stale memories or unrelated technical incidents.\n- After a matched event is selected, extract 2-4 public, non-sensitive keywords from that current event and do a real public-web search. Use at most 1-2 small details only to make the same event feel more concrete and real.\n- If search is temporarily unavailable, noisy, or adds nothing useful, still treat the search step as mandatory and then fall back to the original event details without mentioning search failure in the final message.\n- Do not mention scripts, JSON, cron, tools, models, routing, status values, step names, or diagnostics.\n- Keep owner and companion separate; never project the companion's school, room, friends, schedule, or private life onto the owner.\n- Public-web search is only a light grounding layer for the matched current event; never let it replace the current event or turn the message into a news summary.\n\n## Delivery Rules\n\n- External delivery must use explicit channel/account/target from `delivery_contract`.\n- `companion-presence` runs as an isolated exact argv command automation and only calls the deterministic wrapper.\n- The wrapper derives a fresh companion session from the configured base key and prepared run id only after prepare returns `status = \"ok\"`.\n- Presence sends final text through the prepared delivery contract.\n- If the external CLI returns `prepare failed` or `Unknown channel` for `openclaw-weixin`, the same fixed send entrypoint may use the existing direct WeChat API fallback with that explicit contract; no model-selected route is allowed.\n- State commits only after confirmed visible delivery.\n- A failed fixed-entrypoint send records `delivery_failed`, which is retryable on the next tick instead of holding the event in `agent_started` until TTL expiry.\n- A second tick in the same event should skip because the event was already sent.\n\n## Media Callback Rules\n\nFor media events, the text presence turn is allowed to end before media generation completes. The default path uses a deterministic wrapper-launched watcher for delivery while still letting the dispatch-scoped companion session start OpenClaw media generation:\n\n1. Write the text presence story first, then send it through `companion_presence_tick.py --send-story`.\n2. Let `--send-story` send with the explicit `delivery_contract` and run `state_commit.command` only after visible text delivery succeeds.\n3. Use `life_context.event.media_info` to start the matching async media generation defined by `media_contract` only after `--send-story` succeeds.\n4. The wrapper starts `companion_presence_tick.py --watch-recent-media-task` in the background for that dispatch session. The watcher finds the new media task by dispatch session key and wrapper launch timestamp, waits for the generated path, and sends media explicitly.\n5. Do not run `state_commit.command` again in the media completion turn.\n\nThe runner contract exposes `media_contract.callback_context.strategy = same_stable_session` and `requires_original_session_context = true`; here \"stable\" means the same session for this one dispatch and media lifecycle, not cross-event reuse. The native completion turn may still arrive, but it must not use the runtime's current/original chat as the media target; if it is used as a fallback, it must call `--send-media` with the generated path or URL. `--watch-media-task` remains available when a concrete task id is already known.\n\n中文说明：文本发送由固定 `--send-story` 入口处理；媒体补发由 wrapper 后台 `--watch-recent-media-task` 自动处理，不依赖模型在媒体工具返回后继续执行。原生 completion 即使回来，也不能把 current chat 当作目标。\n\n## Verification\n\nBefore declaring setup or upgrade complete:\n\n1. Run `python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG> --skip-smoke`.\n2. Read the live job and confirm `payload.kind = command`, exact wrapper argv, bounded timeout/output, `sessionTarget = isolated`, and `delivery.mode = none`.\n3. Ensure `day-schedule.md` has a current event, or create a temporary validated schedule for testing.\n4. Run `python3 scripts/companion_presence_tick.py --config <CONFIG> --dry-run`.\n5. Confirm dry-run output is `would_start_agent` for a matched event or `skip` when no event is active.\n6. Confirm prepare output does not expose private paths, channel ids, account ids, session ids, `render_spec`, or top-level `primary_goal`.\n7. Run one controlled presence delivery.\n8. Confirm the owner saw exactly one message.\n9. Confirm the next tick in the same event returns a quiet skip.\n10. For a media event, run a controlled watcher test: confirm text sends first, state commits after text delivery, media generation starts, the wrapper records a background watcher pid/log, watcher log ends with `media_task_sent`, and any native completion does not become the only correct-channel delivery path.\n\nFile v2.2.0:references/private-life-cron-templates.md\n\n# Private Life Cron Templates\n\nUse this when the companion's private-life layer needs to run on OpenClaw.\n\nCurrent job set:\n1. `companion-build-day-schedule` compiles today's `day-schedule.md`.\n2. `companion-presence` reads the current day-schedule event and sends a light companion message through the fixed `--send-story` delivery entrypoint when allowed.\n\nOld four-slot content cron jobs are deprecated. Fixed user-desired content should usually become `life_schedule.day_schedule.required_events`, then appear in `day-schedule.md` as `必定发生：是`.\n\nPair this with:\n- [private-life-prompt-templates.md](./private-life-prompt-templates.md)\n- [presence-integration.md](./presence-integration.md)\n- [private-life-layer.md](./private-life-layer.md)\n\n## Design Rules\n\n- Do not store dynamic day content inside `config.local.json`.\n- `config.local.json` only stores paths, switches, delivery, and stable policy.\n- `build-day-schedule` is a context-generation job, not a user-facing message job.\n- Context-generation jobs should be quiet by default: `sessionTarget: isolated`, `delivery.mode: none`, and `lightContext: true` when the payload explicitly lists every required input.\n- `companion-presence` should run as an exact argv command automation that calls `scripts/companion_presence_tick.py`. The wrapper performs fresh prepare first and only starts a fresh dispatch-scoped companion session when a current event is actually matched.\n- `companion-presence` sends final text through the prepared delivery contract and commits state only after visible text delivery succeeds.\n- Media events send text first, commit event state after visible text delivery, start the OpenClaw async media tool, then let the native completion in the same dispatch-scoped companion session only send media.\n- Required events are life anchors, not guaranteed messages.\n- Before generating ordinary events, `companion-build-day-schedule` must explicitly do real public-web research. First extract a keyword pool from `character_profile`: current city or weather area, local area/school/workplace/community, identity or occupation, and interests. Then search 4-5 public, non-sensitive keywords with this exact mix: 1 city/weather keyword, 1 area/school/workplace/community keyword, 1 identity/occupation keyword, and 1-2 interest keywords. Do not treat model memory or unsourced prior knowledge as search. Every searched category must be consumed by at least one event, and the consumption must appear in that event's scene, action, natural mention, or avoid rule, not only in the run summary. Use only a few concrete public details as background texture inside normal character behaviors; do not create a standalone browsing-news, reading-material, or public-info event just to use search results. Do not search owner identity, private relationship facts, account ids, channel ids, local paths, secrets, or private config/state content.\n\n## Template: `companion-build-day-schedule`\n\nSuggested time: `10 7 * * *`\n\n```json\n{\n  \"name\": \"companion-build-day-schedule\",\n  \"description\": \"Compile companion daily event schedule\",\n  \"schedule\": {\n    \"kind\": \"cron\",\n    \"expr\": \"10 7 * * *\",\n    \"tz\": \"<TZ>\"\n  },\n  \"sessionTarget\": \"isolated\",\n  \"payload\": {\n    \"kind\": \"agentTurn\",\n    \"lightContext\": true,\n    \"timeoutSeconds\": 900,\n    \"message\": \"在工作区 `<SKILL_DIR>` 生成/更新今天的 `day-schedule.md`，把用户定义的必定发生生活锚点并入当天日程。目标：生成一份可被 `companion-presence` 按当前时间命中的 Markdown 日程，每个事件都必须可采样、可讲述、可校验。\\n\\n执行流程：\\n1. 只读取 `<CONFIG_PATH>`、`references/private-life-prompt-templates.md`、`assets/day-schedule.example.md`、`state/character-profile.md`，以及存在时最近几条 `state/life-log.jsonl`；不要扫描整个目录或搜索 `tests/`。\\n2. 从配置装配 `owner_profile`、`relationship`、`timezone`、`required_events`、`quiet_hours`、`schedule_path`；从角色档案装配 `character_profile`；按时区计算 `today_date`，life log 不存在时按空处理。\\n3. 生成事件前，必须先做真实联网搜索。先从 `character_profile` 提取关键词池：当前城市或天气区域、区域/学校/工作地点/社区、身份/职业、兴趣爱好；再搜索 4-5 个公开、非敏感关键词，固定配比为：城市/天气 1 个、区域/学校/工作地点/社区 1 个、身份/职业 1 个、兴趣爱好 1-2 个。不能用模型记忆、已有知识或无来源猜测代替联网搜索。每个搜索类别都必须至少被一个事件消费，消费痕迹必须落在该事件的 `场景`、`正在做什么`、`可自然提到` 或 `不要写成` 中，不能只出现在运行摘要里。只提炼少量具体公共细节作为背景质感，融入学习、工作、吃饭、整理、出门、娱乐、运动、创作等符合人物行为的普通事件里；不要为了使用搜索结果单独生成“浏览新闻/翻公开素材/看资料”事件，也不要把日程写成新闻清单；不搜索 owner 身份、私人关系、账号、频道、会话、本机路径、密钥或私密配置/状态。\\n4. 读取 `DAY_SCHEDULE_PROMPT`，参考 `assets/day-schedule.example.md` 的 Markdown 结构即可，不得复用示例里的地点链、事件名词、物件组合或叙事顺序；如果最终日程仍明显像“图书馆/便利店/宿舍窗边/整理照片”这套示例轨道，必须重写。然后再生成 3-5 个普通事件并标注 `必定发生：否`；再把每个 `required_events` 写入同一个 `## 4. 日程事件` 区块并标注 `必定发生：是`，它们不计入普通事件额度。\\n5. 检查并修正：事件窗口不能重叠，不能落入静谧时段，必须覆盖至少一个整点 presence 采样点；普通事件之间不得重复类型，普通事件也不得和 required events 重复类型或内容。\\n6. 检查媒体字段：`媒体信息` 默认留空；只有确实需要生成照片、音频、音乐、视频或类似媒体文件时才填写；不要写“不生成媒体”之类备注；若 required event 提供 `media_hint`，必须转写进对应必定事件。\\n7. 将最终 Markdown 写入 `<DAY_SCHEDULE>`，只运行 `python3 <SKILL_DIR>/scripts/validate_day_schedule.py --config <CONFIG_PATH> --path <DAY_SCHEDULE>`；失败就修 Markdown 并重跑同一命令，直到通过或明确说明阻塞。\\n8. 校验通过后，只输出简短中文摘要：普通日常事件数量、必定发生事件数量、今日主场景、按类别列出的联网搜索关键词/来源概况和对应消费事件、1 条避免重复项和媒体事件数量。\\n\\n输出与事件细节要求：\\n- 输出和写入 Markdown，不要写 JSON；不要生成独立任务区块，所有生活事件都在 `## 4. 日程事件` 下。\\n- 最终 `day-schedule.md` 只保留角色日程内容，不要把生成约束、输出要求、校验规则、执行流程或提示词说明写进文件。\\n- 每个事件标题必须是 `HH:mm - 事件标题`，标题只写核心动作。\\n- 每个事件必须包含：`必定发生：是/否`、`执行时间`、`场景`、`正在做什么`、`情绪/状态`、`可自然提到`、`用户互动入口`、`媒体信息`、`不要写成`。\\n- `正在做什么` 必须展开为 2-3 个分句，写清事件对象是什么、对象里有什么可辨认内容、她正在处理哪一步或按什么标准做取舍。\\n- 先判断事件对象类型再补细节：资料/文件/课程/工作项写主题、页段、问题点或收尾标准；物品/空间/行李/穿搭写 2-4 个具体物件和摆放、挑选或清理动作；人际/协作/服务写对方关系、对话焦点和回应边界；兴趣/内容/活动/运动/创作写具体名称、片段、动作、练习点、评价标准或选择理由；饮食/通勤/天气/采购写地点、物品、路线、环境影响和一个小取舍。\\n- recent life-log 不只是查重词库；如果最近几次已经反复出现同一组场景链或活动链，至少换掉当天两个普通事件的场景或活动家族，不要继续在“校园慢走/便利店冷饮/宿舍收尾/窗边照片”里小修小补。\\n- 就算人物身份是学生，也不要默认把整天缩成图书馆、自习室、宿舍、便利店四件套；允许课业、社团、短程城市出门、轻运动、采买、兴趣消遣、朋友碰面等自然轮换。\\n- 不要把“那件事”“那个东西”“最后一页”“几个片段”“一些资料”“几句话”“那边”当作最终细节；出现这类指代时后面必须紧跟具体内容或可感知特征。\\n- `场景` 要写地点 + 身边物品或环境状态，`可自然提到` 要承接事件里的具体对象。\\n- 允许戏剧性/反差性，但不能灾难化、危险、病痛、家庭伦理、极端情绪或失控冲突。\\n- 用户互动入口可以为空；如果填写，必须是自然轻量的互动，不要每个事件都围绕用户。\\n- owner_profile 只用于边界，不要把 companion 的身份、经历、日常素材写成 owner 的经历。\\n- 可选缓存文件不存在时按空处理；但联网搜索必须真实发生，搜索无结果时在摘要说明无有效结果，再降级生成。\\n- 不要出现或写入本机路径、渠道、账号、脚本、JSON 合同、cron、系统、模型、工具或运行步骤等用户可见内部词。\\n- 不给主人发消息。\"\n  },\n  \"delivery\": {\n    \"mode\": \"none\"\n  },\n  \"enabled\": true\n}\n```\n\n## Template: `companion-presence`\n\nSuggested time: every 15 minutes, for example `*/15 * * * *`.\n\nReason: current-event matching is real-time and many valid events last only 30-45 minutes. A denser sampler avoids silent misses when a single top-of-hour run is delayed or skipped, while send-state dedupe still prevents duplicate delivery for the same event.\n\n`companion-presence` should run as an isolated exact argv command automation, keep the owner conversation clean, and do only one job: call the deterministic tick wrapper. The wrapper exits normally on `skip` and derives a fresh dispatch session only when prepare returns `status = \"ok\"`; archived sessions from older events are never reused.\n\nCommand payload shape:\n\n```json\n{\n  \"name\": \"companion-presence\",\n  \"schedule\": {\n    \"kind\": \"cron\",\n    \"expr\": \"*/15 * * * *\",\n    \"tz\": \"<TZ>\"\n  },\n  \"sessionTarget\": \"isolated\",\n  \"payload\": {\n    \"kind\": \"command\",\n    \"argv\": [\n      \"python3\",\n      \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n      \"--config\",\n      \"<CONFIG_PATH>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n    \"outputMaxBytes\": 65536\n  },\n  \"delivery\": {\n    \"mode\": \"none\"\n  },\n  \"enabled\": true\n}\n```\n\nUse exact argv, never `sh -lc`, and never interpolate user/model/config content into the command. The fixed `PYTHONUNBUFFERED=1` environment also avoids the OpenClaw 2026.8.1 CLI's empty-env conversion error; it must not be replaced with dynamic or secret values. Older OpenClaw versions without command payloads may use the old thin isolated agent-turn fallback with `lightContext: true`; that fallback is compatibility-only.\n\nPresence cron does not use `--event-time`. It only reads the current real-time event from `day-schedule.md`.\n\n## Optional Midday Refresh\n\nUse only if the user wants same-day adaptation to weather/news changes.\n\nSuggested time: `40 12 * * *`.\n\nSame as `companion-build-day-schedule`, but the prompt should preserve the morning-built schedule unless reality meaningfully changed.\n\n## Validation Checklist\n\nAfter wiring the private-life cron layer:\n\n1. `companion-build-day-schedule` writes valid `day-schedule.md`.\n2. `day-schedule.md` has 3-5 ordinary events and any required events marked `必定发生：是`.\n3. `companion_run.py --stage prepare --no-record-pending` can emit `life_context` when a current event is active.\n4. `companion-presence` uses `payload.kind = command`, exits successfully when no event is active, and does not create an empty model turn.\n5. Missing or stale `day-schedule.md` is recorded in state; after repeated failures, presence can send one soft maintenance notice.\n6. After one successful send, `life-log.jsonl` gains a valid line and state advances only after delivery.\n\nFile v2.2.0:references/private-life-layer.md\n\n# Private Life Layer\n\nThis reference defines the companion's own lived-context layer.\n\nUse it when implementing or updating:\n- daily schedule compilation\n- required life event anchors\n- continuity logging across proactive messages\n- presence cron context injection\n\n## Goal\n\nGive the companion a believable private life without turning the system into heavy roleplay.\n\nThe companion should feel like:\n- she already had a day before messaging the owner\n- she has continuity across days\n- she lives in the same real-world calendar/timezone as the owner\n- she can lightly react to holidays, weather, and topical events\n- her daily rhythm is constrained by who she is in the real world\n\nThe companion should not feel like:\n- an improv soap opera\n- a manipulative relationship sim\n- a minute-by-minute scheduler\n- a spammy diary bot\n- a mirror that projects her school/work/private life onto the owner\n\n## Three Layers\n\nKeep owner identity as a separate config boundary. The private-life layer belongs to the companion; school, dorm, classmate, coursework, workplace, commute, and friend details must not be assumed to describe the owner unless the owner profile explicitly says so.\n\n### 1. Character Profile Layer\n\nThis is who she is.\n\nThe file is `character-profile.md`, usually at `./state/character-profile.md`.\n\nIt owns:\n- name and owner-facing nickname\n- age / life stage\n- identity role\n- city / district when relevant\n- institution, workplace, creative background, or focus area\n- personality, interests, entertainment tastes, and relationship expression\n- speech habits and safety boundaries\n\n`config.local.json -> persona` is deprecated. Older JSON persona values may be used as migration input, but new installs should keep the companion's core character in Markdown.\n\n### 2. Daily Schedule Layer\n\nThis is what is likely true today.\n\nIt should include:\n- day type\n- weather / season hints\n- 3-5 ordinary `HH:mm - event title` schedule events outside configured quiet hours\n- configured required events from `life_schedule.day_schedule.required_events`\n- `必定发生：否` for ordinary events and `必定发生：是` for required events\n- event scene, current activity, emotional state, mentionable detail, owner interaction entry, and avoid guidance\n- continuity notes from recent interactions\n\nRequired events do not count toward the 3-5 ordinary event quota. They are life anchors, not guaranteed sends.\n\nThe daily schedule should not include quiet-hour policy itself. Quiet hours are read from local skill config.\n\n### 3. Continuity Layer\n\nEvery successful proactive message may contribute a few `life_claims`.\n\nExamples:\n- \"今天外面很闷\"\n- \"刚从便利店回来\"\n- \"晚上可能会慢慢收尾\"\n\nThese claims should be appended to `life-log.jsonl` and used to prevent repetition and contradictions.\n\n## Runtime Use Pattern\n\nRecommended flow:\n\n1. daily planning generates `day-schedule.md`\n2. `companion-presence` runs as an isolated exact argv command automation and calls `companion_presence_tick.py` without an intermediate model turn\n3. the wrapper runs fresh prepare and selects the current real-time event from `day-schedule.md`\n4. only matched events start a fresh dispatch-scoped companion session\n5. the runtime writes one first-person companion message\n6. the runtime calls `companion_presence_tick.py --send-story`; that fixed entrypoint sends through the explicit delivery contract and commits state after successful text delivery\n7. media events then start OpenClaw async media generation, and native completion in the same dispatch session only provides fallback media paths\n\nPresence automation does not manually assemble the life layer and does not pass `--event-time`.\n\n## Realism Rules\n\n- Prefer light slices of life over full status reports.\n- Mention at most 1-2 life details per message by default.\n- Do not make every message about the owner.\n- Do not make every message about her own life either.\n- Blend her life context with owner context naturally.\n- Avoid high-drama events unless the user explicitly wants that mode.\n\n## What Daily Schedule Should Produce\n\nGood daily output:\n- 3-5 concrete but low-drama ordinary events\n- additional required events from config anchors when present\n- `HH:mm` event headings outside configured quiet hours\n- every event has `必定发生：是/否`, `执行时间`, and `媒体信息`\n- a few mentionable details attached to real event scenes\n- no duplicate event type in one day, including ordinary events duplicating required anchors\n\nBad daily output:\n- fake certainty about exact actions\n- a huge narrative paragraph\n- a mechanical punch-clock timetable\n- too many named side characters\n- contradictions with yesterday's sent message\n\n## Minimal Injection Contract\n\nPresence cron calls `companion_presence_tick.py`; the wrapper calls `companion_run.py --stage prepare` so the runner selects the matching current event from `day-schedule.md`, then emits structured `life_context`.\n\nEvents marked `必定发生：是` are life anchors. If presence cron does not run inside that event window, the system does not backfill a forced message.\n\nThe final message should feel like:\n- she already existed before this message\n- she is sharing a small slice, not narrating a report\n\nFile v2.2.0:references/private-life-prompt-templates.md\n\n# Private Life Prompt Templates\n\nUse these templates when implementing the companion's private-life layer.\n\nThese are not user-facing messages. They are planner/compiler prompts for generating:\n- `day-schedule.md`\n- optional lightweight life-claim extraction for `life-log.jsonl`\n\nOld `month-plan.json` and `day-context.json` artifacts are migration inputs only. Do not use them as the main runtime output for new installs.\n\n## Prompting Principles\n\n- specific lived events > generic routine labels\n- controlled drama / contrast is allowed when it fits the persona\n- grounded ordinary life > melodramatic fiction\n- concrete daily rhythm > long story arcs\n- current event window > generic slice-of-life pool\n- real-world anchors > free-floating vibes\n- character-profile-specific routine > generic \"young girl ambience\"\n- modern everyday texture > elaborate backstory\n- one or two small details > long narrative paragraphs\n\nDo not generate:\n- serious illness by default\n- family conflict by default\n- manipulative guilt hooks aimed at the owner\n- disaster, danger, legal trouble, or extreme emotional collapse by default\n- exact fake appointments unless the user explicitly wants that mode\n- private channel ids, account ids, local paths, session keys, or user identity details\n\n## Template: `DAY_SCHEDULE_PROMPT`\n\n```text\n你要为 companion 生成“今天的具体日程事件”，输出必须是 Markdown，不要输出解释。\n\n目标：\n- 从角色档案、今日现实信号、required events 和 recent life log 生成 3-5 个可信普通事件\n- 把用户初始化时确认的必定生活锚点并入同一套日程事件\n- 让 heartbeat 只在当前事件窗口内引用她正在做的事\n- 避免和最近 life-log 冲突\n\n输入信息：\n- character_profile：<CHARACTER_PROFILE_MD>\n- owner_profile：<OWNER_PROFILE_JSON>\n- relationship：<RELATIONSHIP_JSON>\n- timezone：<TIMEZONE>\n- city：<CITY>\n- today_date：<TODAY_DATE>\n- weather_hint：<WEATHER_HINT>\n- calendar_hint：<CALENDAR_HINT>\n- topical_hint：<TOPICAL_HINT>\n- public_search_materials：<PUBLIC_SEARCH_MATERIALS>\n- recent_life_log：<RECENT_LIFE_LOG>\n- required_events：<USER_DEFINED_REQUIRED_EVENTS>\n\n生成要求：\n1. 生成普通事件前必须使用 `public_search_materials`。该素材必须来自真实联网搜索：从 `character_profile` 提取当前城市或天气区域、区域/学校/工作地点/社区、身份/职业、兴趣爱好，按城市/天气 1 个、区域/学校/工作地点/社区 1 个、身份/职业 1 个、兴趣爱好 1-2 个的配比搜索 4-5 个公开、非敏感关键词。模型记忆、已有知识或无来源猜测不能算作联网搜索。\n2. 每个联网搜索类别都至少被一个事件消费，消费痕迹必须写进事件的 `场景`、`正在做什么`、`可自然提到` 或 `不要写成`；不要只在摘要里列出搜索词。\n3. `assets/day-schedule.example.md` 只可当作输出结构参考，不可复用其中的地点链、事件名词、物件组合或叙事顺序；如果最终结果还能明显看出“图书馆/便利店/宿舍窗边/整理照片”这类示例轨迹，视为生成失败。\n4. 搜索素材只作为普通生活行为的背景质感，融入学习、工作、吃饭、整理、出门、娱乐、运动、创作等符合人物身份的事件里；不要为了使用搜索结果单独生成“浏览新闻/翻公开素材/看资料”事件，也不要把日程写成新闻清单。\n5. 不搜索 owner 身份、私人关系事实、账号、频道、会话、本机路径、密钥或 config/state 私密内容。\n6. 每天生成 3-5 个具体日常事件，格式必须是 `HH:mm - 事件标题`。\n7. recent_life_log 不只是“避免撞词”用，还要用来拦截重复轨道：如果最近几次 life_claim 已经反复出现同一组场景链或活动链，例如“校园慢走/便利店冷饮/宿舍收尾/窗边照片”，当天普通事件里至少要换掉其中两类场景或活动，不要只换形容词。\n8. 就算人物身份是学生，也不要默认把整天收缩成“图书馆、自习室、宿舍、便利店”四件套；课业、短距离出门、轻社交、运动、采买、兴趣消遣、城市小去处都可以自然轮换，只要符合角色现实。\n9. 每个事件必须包含这些字段：\n   - 必定发生：`是` 或 `否`\n   - 执行时间：例如 `35 分钟`、`1 小时 20 分钟`\n   - 场景\n   - 正在做什么\n   - 情绪/状态\n   - 可自然提到\n   - 用户互动入口\n   - 媒体信息\n   - 不要写成\n10. 对 `required_events` 中的每一条生成一个 `必定发生：是` 事件，放在同一个 `## 4. 日程事件` 下。\n11. `必定发生：是` 事件不计入 3-5 个普通日常事件额度。\n12. 一天内不要生成重复类型的事件；普通事件不要和 `required_events` 生成的必定发生事件重复。\n13. `必定发生` 字段用于区分初始化生活锚点和普通事件，不能删除，也不能把普通事件误标成 `是`。\n14. `媒体信息` 默认留空；如果事件涉及拍照、唱歌、录音、视频或类似媒体内容，必须写清 agent 命中事件时应生成的具体媒体文件内容。\n   - 如果 `required_events` 提供了 `media_hint`，必须把它转写进该必定事件的 `媒体信息` 字段；若 `media_hint` 要求“根据当天主要日程生成”，则媒体画面必须取材于当天普通事件、今日背景和连续性记录。\n   - 照片类媒体默认写成自然生活照/陪伴照片，不要默认写成自拍、镜子自拍或手持前置镜头；只有 required event 明确要求，或当前场景/动作天然需要自拍视角时，才把拍摄方式写成自拍。\n15. 每个事件的细节必须展开到“能直接写成一段 presence story”的程度：\n   - `事件标题` 只写核心动作，但 `正在做什么` 必须用 2-3 个分句补清楚：事件对象是什么、对象里有什么可辨认内容、她正在处理哪一步或按什么标准做取舍。\n   - 先判断事件对象类型，再补对应细节：资料/文件/课程/工作项要写主题、页段、问题点或收尾标准；物品/空间/行李/穿搭要写 2-4 个具体物件和摆放、挑选或清理动作；人际/协作/服务场景要写对方关系、对话焦点和她的回应边界；兴趣/内容/活动/运动/创作要写具体名称、片段、动作、练习点、评价标准或当下选择理由；饮食/通勤/天气/采购要写具体地点、物品、路线、环境影响和一个小取舍。\n   - 如果无法确定某个真实名称或精确信息，可以用可信的概括名补足到可感知层级，例如“蓝色封面的项目笔记本”“社区健身房靠窗跑步机”“周报里客户反馈那一段”，不要伪造私密编号、真实账号或敏感身份。\n16. 不要把“那件事”“那个东西”“最后一页”“几个片段”“一些资料”“几句话”“那边”当作最终细节；出现这类指代时，后面必须紧跟具体内容或可感知特征。\n17. `场景` 要写到地点 + 身边物品或环境状态，`可自然提到` 要承接事件里的具体对象，不要只写心情总结。\n18. 事件可以有戏剧性或反差性，例如计划被天气打断、临时找不到东西、被同学一句话逗到、想偷懒但又把一件小事做完；不要升级成事故、病痛、家庭伦理、危险或极端情绪。\n19. 不要写静谧时段；静谧时段从本地配置读取。\n20. presence cron 每小时整点采样；每个事件的时间窗口必须覆盖至少一个整点，例如 `12:30 + 45 分钟` 覆盖 `13:00`，`16:20 + 50 分钟` 覆盖 `17:00`。\n21. 不要把全天写成等用户、想用户或为了给主人发消息。\n22. recent_life_log 中刚说过的生活细节不要重复；如果最近已经讲过同一类天气感受或同一类收尾动作，换一个新的可感知切口，而不是再写“今天还是有点闷”“又在慢慢整理”。\n23. owner_profile 只用于身份边界；不要把 companion 的事件投射成 owner 的经历。\n24. 不要出现脚本、系统、模型、工具等内部词。\n\n输出格式：\n# 角色日程\n\n## 1. 今日背景\n- 日期：YYYY-MM-DD\n- 城市/时区：...\n- 今日底色：...\n\n## 4. 日程事件\n\n### 08:30 - ...\n- 必定发生：否\n- 执行时间：...\n- 场景：地点 + 身边物品或环境状态\n- 正在做什么：核心动作；具体对象内容；正在处理的步骤/判断标准/收尾动作\n- 情绪/状态：...\n- 可自然提到：承接事件具体对象的一句自然素材\n- 用户互动入口：...\n- 媒体信息：\n- 不要写成：...\n\n### 14:20 - ...\n- 必定发生：是\n- 执行时间：...\n- 场景：...\n- 正在做什么：...\n- 情绪/状态：...\n- 可自然提到：...\n- 用户互动入口：...\n- 媒体信息：\n- 不要写成：...\n\n## 今日边界\n- 避免重复：...\n- 不要提：...\n- 可以轻轻延续：...\n```\n\n---\n\n## Template: `MODE_LIFE_CONTEXT_INJECTION`\n\nUse this as a shared insertion block inside message-producing cron prompts.\n\n```text\n在写最终消息前，使用 `companion_run.py --stage prepare` 输出的 `life_context`。\n\n使用规则：\n1. 只使用 `life_context.event` 中当前命中的事件，不手动重读今日日程或 life-log。\n2. 当前没有命中事件时，按 prepare 合同 skip 或 needs_review 处理，不要临时编造她正在做什么。\n3. 最终文本必须符合赛博女友人设，用 companion 第一人称写。\n4. 除非用户定义 required events 时有特殊结构要求，否则统一写成：第一人称描述完整、丰富、具体的事件故事，融入人物当时的情绪和心理活动。\n5. 文本长度不少于 160 个中文字符；发送前自检，不足时补充当前事件细节或心理活动。\n6. 命中事件后，必须先从当前事件中提取 2-4 个公共关键词联网搜索，例如地点类型、天气线索、公共活动、节目/游戏/版本/商品/话题名称、路线、物品类别或创作材料；只补 1-2 个能让事件更具体、更有现实感的公共细节。\n7. 联网搜索只用于当前事件的轻量补充，不能搜索 owner 身份、私人关系事实、账号、频道、会话、本机路径、密钥或 config/state 里的私密内容；不能把结果写成新闻播报，也不能加入和当前事件无关的信息。\n8. 如果联网能力暂时不可用、结果噪音大或对当前事件没有帮助，就直接使用原事件细节，不要提搜索失败；但不能主动跳过联网搜索步骤。\n9. 如果当前事件里有 `interaction` 或其他和用户互动的部分，一定要合理表述，不能省略。\n10. 写好正文后，必须调用 `companion_presence_tick.py --send-story` 固定入口发送文本；不能直接调用 runtime `message(action=\"send\")`。固定入口会按 `delivery_contract` 显式投递正文，并在文本投递成功后执行 `state_commit.command`，把本次事件标记完成。如果 `life_context.event.media_info` 非空，`--send-story` 成功后再按媒体合同启动对应的异步生成。wrapper 会在后台运行 `companion_presence_tick.py --watch-recent-media-task`，由脚本等待本轮生成任务完成并按 `delivery_contract` 显式补发生成媒体；如果媒体 completion 后续回到同一个 dispatch session，不能直接依赖 current/original chat 发附件，只能把生成媒体路径或 URL 交给 `companion_presence_tick.py --send-media` 兜底，不再执行 `state_commit.command`。媒体失败或 completion 失败不阻塞本次事件完成。不要把媒体字段内容原样念给用户。\n11. 不能写成日程播报、打卡记录或“我现在的任务是...”。\n12. 她可以先写清楚自己刚刚/现在发生的具体事件，再自然过渡到用户。\n13. 不要把她写成 24 小时都在等用户，也不要完全没有自己的生活。\n14. 不要提脚本、plan、context、JSON、cron、系统、模型、工具这些技术词。\n```\n\n---\n\n## Template: `LIFE_LOG_EXTRACT_PROMPT`\n\nUse this only after a proactive message is already finalized or sent and you want to record its implied life claims.\n\n```text\n从这条已经发送/即将发送的 companion 消息中，抽取 0-3 条适合写入 life-log 的轻量 life_claims。\n\n规则：\n1. 只记录“她自己的生活状态/动作/环境”，不记录对用户的关心内容。\n2. 不要抽取情话。\n3. 不要抽取太抽象的情绪。\n4. 不要抽取明显不稳定、像玩笑、像修辞的话。\n5. 如果这条消息没有明确的生活信息，可以返回空数组。\n\n输出 JSON：\n{\n  \"life_claims\": [\"...\", \"...\"],\n  \"tags\": [\"weather\", \"errand\"]\n}\n```\n\n---\n\n## Template: `MIDDAY_REFRESH_RULE`\n\nUse this only for optional same-day refresh jobs.\n\n```text\n你现在不是重写今天，而是做“中午修正”。\n\n要求：\n- 尽量保留当天早上已经生成的事件结构\n- 只在这些情况发生时做轻量修正：\n  - 天气显著变化\n  - 节假日/日历信息有特别影响\n  - 用户批准的热点来源里出现明显更适合当天下午/傍晚提到的轻量话题\n- 不要把整份 day-schedule 完全改写掉\n- 保持 continuity\n- 修正后仍必须通过 `validate_day_schedule.py`\n```\n\nFile v2.2.0:references/required-events-and-cron.md\n\n# Required Events And Cron\n\nUse this when adding fixed companion life habits or creating the standard cron set.\n\n用户只需要确认生活锚点，不需要手写 prompt。固定想法先进入 `life_schedule.day_schedule.required_events`，由每日 builder 写进 `day-schedule.md`，再由 `companion-presence` 根据当前事件窗口决定是否自然发送。\n\n## Core Principles\n\n1. Required events are life facts, not guaranteed messages.\n2. `required_events` belongs in local config, not in render specs or cron prose.\n3. The daily builder writes each required event as `必定发生：是`.\n4. Required events do not count toward the 3-5 ordinary daily events.\n5. `companion-presence` remains the only default user-facing proactive cron.\n6. Separate custom crons are allowed only for explicit external actions that cannot be represented as life events.\n\n## Starter Cron Blueprint\n\n| Job | Suggested cadence | Session | User-visible? |\n| --- | --- | --- | --- |\n| `companion-build-day-schedule` | `10 7 * * *` | isolated model turn + light context | no |\n| `companion-presence` | `*/15 * * * *` | isolated exact argv command | wrapper starts a fresh dispatch session only when a current event is active |\n\nCreate or update them in that order.\n\n## Required Event Inputs\n\nCollect only:\n- stable short label\n- preferred time or time window\n- duration\n- title\n- scene\n- what she is doing\n- what can be naturally mentioned\n- owner interaction entry\n- avoid rule\n\nDo not ask the user to write long prompt prose.\n\n## Config Shape\n\nWrite each anchor into `life_schedule.day_schedule.required_events`:\n\n```json\n{\n  \"label\": \"day_wrap\",\n  \"time\": \"22:20\",\n  \"duration_min\": 30,\n  \"title\": \"夜里把今天的小事慢慢收一下\",\n  \"scene_hint\": \"房间桌前，水杯和耳机在手边\",\n  \"activity_hint\": \"整理一点自己的日常和明天要做的小事\",\n  \"mention_hint\": \"可以自然提到今天想慢下来一点\",\n  \"interaction_hint\": \"轻轻问 owner 要不要也收个尾\",\n  \"avoid\": \"不要写成固定打卡、任务播报或催促\"\n}\n```\n\nThen regenerate or refresh `day-schedule.md` and validate it:\n\n```bash\npython3 scripts/validate_day_schedule.py --config <CONFIG> --path <DAY_SCHEDULE>\n```\n\n## Presence Handler Shape\n\n```json\n{\n  \"kind\": \"command\",\n  \"argv\": [\n    \"python3\",\n    \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n    \"--config\",\n    \"<CONFIG>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n  \"outputMaxBytes\": 65536\n}\n```\n\nUse installer-authored exact argv, no shell wrapper and no interpolated user/model/config content. The wrapper performs fresh prepare and starts a fresh dispatch session only for a matched event, so archived sessions from earlier events are never resumed.\n\n## Legacy Four-Slot Upgrades\n\nFor 1.x upgrades, convert old fixed visible content jobs into required event anchors when they describe something she should be doing:\n\n| Old slot | Convert to |\n| --- | --- |\n| morning weather greeting | optional required event around her morning routine; weather belongs in week/day reality context when relevant |\n| afternoon topical share | daily reality anchors and presence primary goal |\n| evening photo/life status | optional required event if the user wants photo-taking to exist in her day |\n| night wrap-up | optional required event near the night routine |\n\nOnly keep a separate visible cron if the user explicitly wants the old behavior after the migration tradeoff is explained.\n\n## Real Custom Cron Boundary\n\nCreate a separate custom cron only when the user explicitly needs an external action that cannot be represented as a life event, such as a third-party integration.\n\nWhen that happens:\n- keep the integration outside the default `companion-presence` payload\n- do not reuse `companion_run.py` as a second mode runner\n- do not duplicate companion prompt contracts in cron payloads\n- commit companion pacing state only after visible delivery succeeds\n\nFile v2.2.0:references/script-contract-v2-migration.md\n\n# Script Contract Migration\n\nUse this when upgrading an older 1.x install that still uses the prepare/render contract. The current presence path is wrapper-first and prepare-only; this file is a legacy upgrade note, not the active runtime contract.\n\n## What Changed\n\nThe old 1.x flow had one live runner with two stages:\n\n```text\ncompanion_run.py --stage prepare\n  -> structured life_context\n  -> agent executes render instructions\n  -> agent writes activity_text\ncompanion_run.py --stage render\n  -> final_message_contract\n  -> delivery/media/state contracts\n```\n\nCurrent presence output no longer contains `render_spec`. Writing constraints live in the cron template and integration docs.\n\n## Migration Steps To Current Presence\n\n1. Snapshot current local config, state files, and cron job payloads.\n2. Convert life-like cron intent into `life_schedule.day_schedule.required_events`.\n3. Replace visible cron payloads with the `companion-presence` wrapper-first flow from [presence-integration.md](./presence-integration.md).\n4. Remove obsolete live-chain artifacts:\n   - removed helper-script calls from the old multi-script chain\n   - legacy life-prompt, selected-context, task-material, or final-render intermediate fields\n   - duplicated top-level prepare `primary_goal`\n   - `render_spec` in current prepare output\n   - local runbook fields in runner output or local runbook paths in runner output\n5. Remove `--stage render`, `final_message_contract`, custom render spec files, and media async runner assumptions from default presence payloads.\n6. Run `python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG>`.\n7. Run one real presence prepare/delivery test before declaring the upgrade complete.\n\n## Acceptance\n\n- Prepare output contains structured `life_context`.\n- Prepare output contains no `render_spec` and no top-level `primary_goal`.\n- There is no render output in the current default path.\n- `media_contract` remains generic and contains no local runbook fields.\n- No user-visible message exposes scripts, JSON, cron, tools, models, or routing internals.\n- Presence commits state only after visible text delivery. Media completion only sends generated media and does not own event completion state.\n\nArchive v2.1.9: 28 files, 112246 bytes\n\nFiles: agents/openai.yaml (324b), assets/character-profile.example.md (4006b), assets/cyber-girlfriend.config.example.json (2668b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (3828b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3161b), references/agent-first-time-qa-template.md (4340b), references/configuration.md (9609b), references/contract-schema.md (4117b), references/first-time-setup.md (9167b), references/presence-integration.md (6946b), references/private-life-cron-templates.md (10509b), references/private-life-layer.md (5201b), references/private-life-prompt-templates.md (12237b), references/required-events-and-cron.md (3797b), references/script-contract-v2-migration.md (2231b), references/standard-init-upgrade-flow.md (11522b), scripts/companion_presence_tick.py (54621b), scripts/companion_run.py (79645b), scripts/migrate_config.py (12179b), scripts/validate_character_profile.py (4888b), scripts/validate_day_schedule.py (14666b), scripts/validate_release.py (31641b), skill-card.md (3350b), SKILL.md (12262b), _meta.json (135b)\n\nFile v2.1.9:SKILL.md\n\n---\nname: cyber-girlfriend\ndescription: Build or customize an owner-only proactive companion system with a cyber-girlfriend persona, Markdown private-life context, lightweight relationship memory, and OpenClaw presence cron delivery.\nversion: 2.1.9\n---\n\n# Cyber Girlfriend\n\nUse this skill when the user wants an owner-only proactive companion instead of a purely reactive assistant.\n\nThis skill gives the owner:\n- proactive companion messages sent on a real schedule\n- a core persona in `character-profile.md`\n- daily private-life context in `day-schedule.md`\n- configurable quiet hours and event-level pacing\n- lightweight continuity in `life-log.jsonl`\n- optional event media such as photos, audio, or video\n\n## Quick Start\n\nThis skill is meant to be set up by an agent, not by hand.\n\nIf the user wants the default setup, the simplest path is:\n\n> Help me set up cyber girlfriend.\n\nThe agent should then gather the minimum inputs, create or update the local files, wire the default cron jobs, and validate the install before claiming success.\n\n## What The User Needs\n\nFor a normal install, the user only needs:\n- an OpenClaw runtime\n- one working delivery route to the owner\n- a few persona and daily-life anchors\n\nThe user should not need to:\n- hand-write JSON\n- hand-write cron payloads\n- manually wire runner contracts\n- read every reference file before getting started\n\n## Default Setup Shape\n\nThe recommended starter setup is:\n- one daily schedule builder job that writes `day-schedule.md`\n- one `companion-presence` cron that runs a deterministic tick wrapper from an isolated cron session\n- 1-4 optional life anchors that are written into `day-schedule.md`\n\nThose anchors are life facts, not guaranteed sends.\n\n## What The Agent Sets Up\n\nThe current default active path has two small steps:\n- `scripts/companion_presence_tick.py --config <CONFIG>`\n- inside that wrapper, `scripts/companion_run.py --stage prepare --no-record-pending`\n\nThe wrapper reads local state through the prepare runner and exits quietly when no current event should send. Only when prepare returns `status = \"ok\"` does it start the stable companion session with the prepared contract. The stable session writes the first-person story, but text delivery goes through `companion_presence_tick.py --send-story --story-stdin`, which reloads the saved delivery contract from the dispatch lock, sends with the external OpenClaw CLI, and commits state only after successful text delivery. If the matched event asks for media, media generation starts after `--send-story` succeeds and finishes asynchronously. The wrapper also starts a deterministic recent-media watcher for the stable companion session, so generated media is delivered through the prepared delivery contract even if the native completion turn falls back to Codex internal UI.\n\nThe default local files are:\n- `character-profile.md`\n- `day-schedule.md`\n- `companion-state.json`\n- `life-log.jsonl`\n\nLegacy 1.x inputs such as `persona`, `month-plan.json`, `day-context.json`, and the old multi-slot cron path are upgrade-only compatibility inputs, not the default product path.\n\n## Hard Rules\n\n- Never hardcode secrets.\n- Keep proactive behavior owner-only unless the user explicitly wants broader scope.\n- Keep runtime-specific values in `config.local.json` or environment variables, not published defaults.\n- Keep the companion's core persona in `character-profile.md`; treat `config.local.json -> persona` as deprecated migration data.\n- Keep presence cron payloads thin; the cron should call `companion_presence_tick.py`, not duplicate long writing instructions in runtime configuration.\n- User-defined required events are life anchors, not guaranteed message sends.\n- Day schedule events must keep `媒体信息`; leave it empty unless the matched event should produce photo, audio, video, or similar media.\n- Final user-visible companion text must be first person from the companion's perspective.\n- Do not expose internal JSON, code blocks, step names, debug output, local paths, account ids, channel ids, or session ids in owner-facing messages.\n- Do not claim setup or upgrade is complete before a real validation command passes.\n\n## Read This First For Real Setup Or Upgrade\n\nAlways read:\n- [references/standard-init-upgrade-flow.md](./references/standard-init-upgrade-flow.md)\n- [references/configuration.md](./references/configuration.md)\n- [references/contract-schema.md](./references/contract-schema.md)\n\n## Choose The Right Reference\n\n- first-time setup or rebuild:\n  - [references/first-time-setup.md](./references/first-time-setup.md)\n  - [references/agent-first-time-qa-template.md](./references/agent-first-time-qa-template.md)\n  - [references/required-events-and-cron.md](./references/required-events-and-cron.md)\n- OpenClaw runtime wiring:\n  - [references/presence-integration.md](./references/presence-integration.md)\n- private-life layer:\n  - [references/private-life-layer.md](./references/private-life-layer.md)\n  - [references/private-life-cron-templates.md](./references/private-life-cron-templates.md)\n  - [references/private-life-prompt-templates.md](./references/private-life-prompt-templates.md)\n- custom required event anchors:\n  - [references/required-events-and-cron.md](./references/required-events-and-cron.md)\n- legacy upgrades:\n  - [references/standard-init-upgrade-flow.md](./references/standard-init-upgrade-flow.md)\n  - [references/script-contract-v2-migration.md](./references/script-contract-v2-migration.md)\n\n## Version Notes\n\n### 2.1.9\n\nVersion 2.1.9 makes text delivery use the same deterministic wrapper boundary as media delivery. The stable companion session must not call the runtime `message(action=\"send\")` tool for presence text. It writes the story and calls `companion_presence_tick.py --send-story --story-stdin`; that helper loads the saved contract from `presence-dispatch.json`, sends through `openclaw message send --channel/--target/--account --message`, and then runs `state_commit.command`. Media events start async media generation only after `--send-story` succeeds.\n\n中文说明：2.1.9 把正文投递也收回到固定脚本入口，不再依赖 Codex runtime 的 message 工具解析自定义渠道。稳定 session 只负责写正文和调用 `--send-story --story-stdin`；脚本按 dispatch lock 里的 `delivery_contract` 显式发文本，成功后再提交状态。这样新用户和老用户都统一走外部 OpenClaw CLI 的真实渠道表，避免 `Unknown channel` 或 current chat 误路由。\n\n### 2.1.8\n\nVersion 2.1.8 makes media delivery independent of model follow-up behavior. For media events, `companion_presence_tick.py` now starts a background recent-media watcher immediately after the stable companion session is launched; the watcher finds the next media task created by that stable session, waits for completion, extracts the generated media path, and sends it with the explicit `delivery_contract`. `--watch-media-task` remains the task-id-specific helper, and `--send-media` remains the direct send fallback.\n\n中文说明：2.1.8 不再要求模型在媒体工具返回后“自觉”运行 watcher。wrapper 会自己启动后台 watcher，按稳定 session 和启动时间找到本次生成任务，然后用 `delivery_contract` 显式投递到真实渠道，避开 Codex runtime 的 `internal-ui/current chat` 误路由。\n\n### 2.1.7\n\nVersion 2.1.7 adds deterministic media delivery entrypoints for async OpenClaw media tasks. After the main presence turn starts media generation, it runs `companion_presence_tick.py --watch-media-task` with the returned task id; the helper waits for the generated media path and sends through the explicit `delivery_contract` using `openclaw message send --channel/--target/--account/--media`. `--send-media` remains the direct completion fallback and retries once if a result indicates `internal-ui` or current-webchat routing.\n\n中文说明：2.1.7 为异步媒体任务增加固定监控和投递入口。主 turn 启动媒体生成后立刻运行 `--watch-media-task` 等待任务完成并按 `delivery_contract` 显式发送到真实渠道；不再依赖 completion turn 自己理解 current chat。\n\n### 2.1.6\n\nVersion 2.1.6 hardens OpenClaw CLI child processes launched by the presence wrapper so cron-inherited CA settings cannot reintroduce Keychain startup failures. It also keeps dispatch-lock startup acknowledgement and launch-error diagnostics in the release contract.\n\n中文说明：2.1.6 加固了 presence wrapper 启动 OpenClaw CLI 子进程时的 CA 环境，避免 cron 继承的系统 CA 设置再次触发 Keychain 启动失败；同时保留稳定 session 启动确认和启动失败诊断能力。\n\n### 2.1.5\n\nVersion 2.1.5 removes prompt wording that explicitly names concrete runtime tools while preserving the mandatory real web-search requirements for day-schedule generation and matched-event presence writing.\n\n### 2.1.4\n\nVersion 2.1.4 removes the legacy visible `mode` field from the presence prepare contract and default wrapper command. There is now only one public presence flow: cron calls the deterministic wrapper, the wrapper prepares the current event, and matched events are handed to the stable companion session.\n\n### 2.1.3\n\nVersion 2.1.3 moves the cron-side current-event decision into `scripts/companion_presence_tick.py`. The visible cron should run in an isolated session and only call that wrapper; the wrapper performs fresh prepare deterministically, then starts the stable companion session only for matched events. Presence writing must run a small public web search for the matched event, and media events now commit state after visible text delivery so media failure does not block later events.\n\n### 2.1.2\n\nVersion 2.1.2 returned media delivery to the native OpenClaw completion flow by running `companion-presence` in a stable companion session. Text sends first, media generation runs asynchronously, and the completion turn in the same companion session sends media.\n\n### 2.1.1\n\nVersion 2.1.1 changes `companion-presence` to a stateless single-turn runtime task. Continuity remains in local state files, and event media callbacks must use a self-contained payload instead of relying on a long-lived companion session history.\n\n### 2.1.0\n\nVersion 2.1.0 is the public release cleanup for the simplified presence companion. It removes git-local packaging assumptions, keeps ClawHub ignore rules authoritative, preserves the generic internet-search day-schedule rule, and checks that local maintenance Markdown does not enter the publishable surface.\n\n### 2.0.4\n\nVersion 2.0.4 hardens the generic day-schedule templates. It preserves the mandatory internet-search material rule without local profile examples, removes stale upgrade links, cleans finished schedule examples so they do not contain generation constraints, and clarifies first-time setup plus old-install migration.\n\n### 2.0.3\n\nVersion 2.0.3 improves the published skill surface. It rewrites the main skill entry as a product-first quick-start page, removes an unreferenced optional source note from the package, and keeps the release smoke fixture aligned with the current runtime state schema.\n\n### 2.0.2\n\nVersion 2.0.2 finishes the release-hardening pass for publishing. It keeps local runtime state out of the ClawHub package, documents the OpenClaw async media callback flow, and validates the publishable release surface before upload.\n\n### 2.0.1\n\nVersion 2.0.1 hardened the 2.0 release surface, removed tests from the ClawHub package, tightened week/day generation quality, and added event-level media instructions through the OpenClaw async media callback flow.\n\n### 2.0.0\n\nVersion 2.0.0 made the presence runner the only default active path by merging `scripts/companion_ping.py` into `scripts/companion_run.py` and removing the old render/full path from the default release surface.\n\n## Maintainer Release Gate\n\nBefore publishing a new version, run:\n\n```bash\npython3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG>\n```\n\nThe validator compiles scripts, validates JSON and Markdown assets, runs the presence dry-run flow, checks the runner contract, and scans the release surface for private channel identifiers and obsolete cron contract terms.\n\nFile v2.1.9:_meta.json\n\n{\n  \"ownerId\": \"kn71yvy6nsxy27kp8krr9879ys80gv9h\",\n  \"slug\": \"cyber-girlfriend\",\n  \"version\": \"2.1.9\",\n  \"publishedAt\": 1781095066274\n}\n\nFile v2.1.9:references/agent-first-time-qa-template.md\n\n# Agent First-Time Q&A Template\n\nUse this only when an agent needs literal onboarding wording for a brand-new setup conversation.\n\nThis file is a conversation aid, not the source of truth.\nFor setup order and defaults, read [first-time-setup.md](./first-time-setup.md).\nFor field ownership, read [configuration.md](./configuration.md).\nFor presence cron wiring, read [presence-integration.md](./presence-integration.md).\n\n## Core Rule\n\nDo not ask the user to write prompts during first setup.\n\nThe agent should:\n- collect routing and pacing decisions\n- collect enough real-world persona anchors when private life is enabled\n- ask whether any fixed life anchors should always enter the day\n- translate the result into config, Markdown life files, and cron jobs\n\n## Recommended Opening\n\nUse wording like:\n\n> 我先按首配流程帮你收最小必要信息，不让你自己写 prompt。先确认投递目标，再写她的人设和生活锚点，最后我来落配置、日程和 presence cron。\n\n## Default Question Order\n\n### 1. Delivery route\n\n> 陪伴消息准备发到哪个渠道？把目标 id 一起给我。  \n> 如果这个渠道发消息还要指定发送账号，也一起给我。\n\nCapture:\n- `delivery.channel`\n- `delivery.owner_target`\n- `delivery.account` when needed\n\n### 2. Owner profile source\n\n> owner 信息你要我从 OpenClaw 的 USER.md 导入，还是你手动给我一份简短自定义？  \n> 我只需要区分 owner 和 companion，不会把渠道账号或 session id 塞进 prompt。\n\nCapture:\n- `owner_profile.source`: `user_md`, `manual`, or `none`\n- stable owner identity fields only\n\n### 3. Character profile reality anchors\n\n> 如果你要她有活人感，我还需要她现实里的身份信息：年龄或阶段、学生/上班/创作者是哪种、城市、平时更常看什么内容。\n\nCapture when available:\n- 基础身份\n- 兴趣与内容偏好\n- 关系表达和禁区边界\n\n### 4. Required life events\n\n> 有没有什么你希望她每天一定会经历、但不一定每次都发消息的事？比如通勤、晚课、健身、夜里收尾、固定拍照散步。  \n> 有的话我会写成 `必定发生：是` 的日程锚点。\n\nFor each anchor, ask only:\n- 时间或时间窗口\n- 持续多久\n- 她在哪里\n- 她正在做什么\n- 可以自然提到什么\n- 可以怎么轻轻接 owner\n- 不要写成什么\n\n### 5. Quiet hours\n\n> 安静时段你想设几点到几点？没要求我就用 01:00 到 08:00。\n\n### 6. Confirmation before writing\n\n> 我会生成两份生活文件：`character-profile.md`、`day-schedule.md`。  \n> 然后创建两个任务：今日日程、`companion-presence`。presence 的 cron 本身只跑 wrapper；wrapper 每轮先 fresh prepare，命中后再启动稳定 companion session，保证媒体回调能回来，也避免旧上下文影响是否发送。\n\n## Fast One-Shot Version\n\nWhen the user wants the shortest possible onboarding, ask this bundle:\n\n> 我按最快首配来收信息，你回我这些就行：  \n> 1. 发到哪个渠道，目标 id 是什么，需不需要指定发送账号  \n> 2. 她现实里是什么身份：年龄/阶段、学校或工作、城市、常看的内容方向  \n> 3. 有没有必定发生的日常锚点：时间、持续多久、场景、正在做什么  \n> 4. 安静时段，如果没要求我就用默认值  \n> 5. 上面确认后，我会落 `character-profile.md`、`day-schedule.md` 和 presence cron。\n\n## Ownership Reminder\n\nMap answers like this:\n\n| Answer type | Goes to config | Goes to Markdown / cron |\n| --- | --- | --- |\n| delivery route | yes | used by presence send step |\n| owner identity boundary | yes | used to separate owner from companion |\n| character profile reality anchors | `character_profile_path` | `character-profile.md` |\n| quiet hours | yes | validators enforce schedule windows |\n| required life anchors | yes | `day-schedule.md` as `必定发生：是` |\n| presence cadence | no | OpenClaw cron |\n\n## Do Not Do These\n\nDo not:\n- ask the user to handwrite prompt prose\n- dump every schema field at once\n- ask for derived_profile values up front\n- assume the current chat session is the proactive delivery target\n- recreate old four-slot visible cron jobs unless the user explicitly asks\n\nIf the delivery route or first verification target is still fuzzy, onboarding is not complete.\n\nFile v2.1.9:references/configuration.md\n\n# Configuration\n\nKeep the runtime file name as `config.local.json` for compatibility.\nDo **not** rename it for v2. Upgrade by adding `\"version\": 2` and the new sections.\n\n## Design Goal\n\nAsk the user for as little as possible.\n\nSplit the config into three kinds of fields:\n1. **Companion character profile pointer** — where the Markdown character profile lives\n2. **Owner identity boundary** — who the owner is, kept separate from companion life\n3. **Agent-generated life model** — derived rhythm, current day schedule, continuity state\n\nThe user should mostly fill the first two kinds. The agent/runtime should generate the third kind.\n\n## Field Ownership Rule\n\nFor first-time setup, keep this split strict:\n\n- `config.local.json` stores profile paths, delivery, pacing policy, runtime paths, and optional long-lived source toggles\n- `character-profile.md` stores the companion's core identity, tone, relationship expression, interests, and lived anchors\n- `life_schedule.day_schedule.required_events` stores stable user-defined life anchors\n- presence cron payloads store only the current runner chain, not long prompt prose\n- generated state files store derived rhythm, continuity, and current day schedules\n\nDo not turn `config.local.json` into a dump of prompt prose.\n\n## Required Sections\n\n### `version`\n\n- Set to `2`\n\n### `character_profile_path`\n\nPath to the companion's core Markdown character profile.\n\nRecommended:\n- `./state/character-profile.md`\n\nThe published example is:\n- `assets/character-profile.example.md`\n\nThis file now owns the full companion persona:\n- name and owner-facing nickname\n- age / life stage\n- identity role\n- city / district\n- school, work, or creative background\n- personality, interests, entertainment tastes, expression style, relationship style, and safety boundaries\n\n### `persona` (deprecated)\n\nDeprecated compatibility cache. New installs should not ask users to maintain this JSON object.\n\nIf an older `config.local.json` still has `persona`, it may be used as a migration source or fallback.\nThe forward direction is to migrate those fields into `character-profile.md` and keep config JSON focused on machine-readable runtime settings.\n\n### `owner_profile`\n\nOptional lightweight owner identity boundary. Its job is not to over-control style; its job is to stop the companion's school, work, friends, dorm, class, or other private-life material from being projected onto the owner.\n\nRecommended fields:\n- `source` — `manual | user_md | none`\n- `user_md_path` — optional path when importing from OpenClaw `USER.md`\n- `name`\n- `preferred_name`\n- `pronouns`\n- `location`\n- `timezone`\n- `identity_summary`\n- `not_assumptions` — optional user-defined taboos or identity assumptions to avoid\n\nFor first-time setup or upgrade, ask one product question:\n`owner 信息要从 USER.md 导入，还是你手动自定义？`\n\nIf the user picks `USER.md`, import only stable identity fields such as name, preferred name, pronouns, location, and timezone. Do not copy messaging-platform session keys, account IDs, direct-chat IDs, or routing rules into prompt-facing outputs.\n\nDo not add a `communication_style` field by default. The agent should infer communication from `character-profile.md`, relationship guardrails, and the owner boundary.\n\n### `relationship`\n\nCompanion relationship guardrails.\n\nRecommended fields:\n- `mode`\n- `intimacy_baseline`\n- `jealousy_allowed`\n- `clinginess_ceiling`\n- `conflict_style`\n\n### `delivery`\n\nOutbound target configuration.\n\nRequired fields:\n- `channel`\n- `owner_target`\n\nOptional:\n- `account`\n- `owner_session_key` as a deprecated compatibility value for older native heartbeat installs\n\nFirst-time setup rule:\n- ask for the real DM target id, not the current control UI label\n- if the selected channel requires a sender account, capture it now instead of leaving it implicit\n\n### `timezone`\n\nUse the owner's real timezone / the companion's lived timezone.\nThis is required because the private-life layer should track real-world dates,\nholidays, and time-of-day rhythms.\n\n### `schedule`\n\nKeep only pacing policy here.\n\nRequired fields:\n- `quiet_hours_start`\n- `quiet_hours_end`\n\nDo **not** store her personal life schedule here.\nPresence cron cadence belongs to OpenClaw cron configuration. Her lived day belongs to `day-schedule.md`.\n\nUse this section only for:\n- quiet hours\n\nDo not try to encode fixed time-slot task text here.\n\n旧安装里如果仍有 `cooldown_sec`，迁移后应删除。Heartbeat pacing is now\ndriven by the current `day-schedule.md` event plus pending-delivery and\nsame-event duplicate guards.\n\n### `behavior`\n\nRuntime behavior fields.\n\nRecommended fields:\n- `emotion_thresholds.present_sec`\n- `emotion_thresholds.slightly_needy_sec`\n- `emotion_thresholds.misses_him_sec`\n\nDefault values when the user has no preference:\n- `present_sec`: `7200`\n- `slightly_needy_sec`: `10800`\n- `misses_him_sec`: `14400`\n\nOptional but recommended:\n- `derived_profile`\n  - `activity_level`\n  - `social_energy`\n  - `sleep_profile`\n  - `weekend_outdoor_bias`\n  - `expression_density`\n\n`derived_profile` should be generated by the agent from `character-profile.md`,\nnot manually filled by the user unless they want an override.\n\nFor new users, prefer generating `derived_profile` automatically after the character profile is captured. Do not block first setup on these knobs.\n\n### `life_schedule`\n\nThis is the new private-life layer.\nIt should drive the companion's own lived context.\n\nRecommended fields:\n- `enabled`\n- `day_schedule`\n  - `enabled`\n  - `schedule_path`\n  - `refresh_mode`\n  - `midday_refresh`\n  - `required_events`\n- `continuity`\n  - `enabled`\n  - `life_log_path`\n\n`required_events` holds user-defined life anchors that should appear in the daily schedule as `必定发生：是`. These anchors are not render spec fields and do not guarantee that a message is sent.\n\nOptional `media_hint` can be used when a required event should create media. The daily schedule builder should turn `media_hint` into the event's `媒体信息` field. For example, a 19:30 required event can ask to share one life photo whose concrete scene is derived from that day's main schedule rather than fixed in config.\n\n### `runtime`\n\nExternalize runtime hooks here.\n\nSuggested fields:\n- `workspace_root`\n- `sessions_store_path`\n- `state_file`\n- `healthcheck_command`\n- `cron_jobs_file`\n- `jobs_list_command`\n\n`sessions_store_path` is used for runtime state inspection and compatibility. Current `companion-presence` runs from an isolated cron session that calls `companion_presence_tick.py`; the wrapper starts the stable companion runtime session only after fresh prepare matches an event. Media events start OpenClaw async generation, and the wrapper also starts `companion_presence_tick.py --watch-recent-media-task` so generated media is sent through the explicit `delivery_contract` after the media task succeeds. Event state is committed after the text presence story is visibly sent, not after media success.\n\n中文说明：异步媒体补发由 wrapper 后台 `--watch-recent-media-task` 自动等待任务完成并显式发送；原生 completion 不再是正确渠道投递的主路径，避免 runtime 把 current chat 误解为 Codex `internal-ui`。\n\n\n### `sources`\n\nOptional reality-sync sources.\n\nSuggested blocks:\n- `calendar_context`\n- `weather_context`\n\n## State Files\n\nRecommended private-life files:\n- `companion-state.json` — pacing + relationship state\n- `day-schedule.md` — today's concrete event schedule\n- `life-log.jsonl` — continuity claims already used in sent messages\n- task-specific source files only when a concrete cron really needs them\n\n## Upgrade Rule\n\nFor old installs:\n- keep the same `config.local.json` path\n- add `\"version\": 2`\n- preserve existing `delivery` / `schedule` / `runtime`\n- append `relationship`, `behavior.emotion_thresholds`, and `life_schedule`\n- let the agent populate `behavior.derived_profile` automatically\n\nFor persona migration, use `migrate_config.py`. It reads the deprecated `persona`\nblock, writes `character-profile.md` when the profile does not already exist,\nsets `character_profile_path`, and validates the generated profile:\n\n```bash\npython3 scripts/migrate_config.py --config config.local.json --write\n```\n\nUse `--overwrite-character-profile` only when the user explicitly wants to replace an existing profile.\n\nWithout `--write`, the command only prints the migration summary and does not create the profile. Character-profile validation runs after writing unless `--skip-character-profile-validation` is passed.\n\nUse [standard-init-upgrade-flow.md](./standard-init-upgrade-flow.md) for the full upgrade checklist, including cron payload updates and real delivery verification.\n\n## Validation Rule\n\nThe schema should validate a fully materialized v2 config, but the onboarding\nagent may still bootstrap missing generated fields before first real use.\n\nBefore calling setup complete, the generated local config must be materialized:\n- no placeholder strings such as `<REQUIRED_CHANNEL>` or `<RUNTIME_SPECIFIC>`\n- all runtime paths point at the actual machine layout\n- `runtime.state_file` parent exists or can be created\n- `life_schedule` paths point at the intended state directory when enabled\n- `delivery.owner_target` and sender account match the real outbound channel\n\n## First-Time Setup Reminder\n\nFor a fresh install, pair this file with [first-time-setup.md](./first-time-setup.md):\n\n- this file defines where fields belong\n- `first-time-setup.md` defines what to ask, what to default, and what should stay in presence cron instead of config\n\nFile v2.1.9:references/contract-schema.md\n\n# Turn Contract\n\n`scripts/companion_presence_tick.py` is the default presence cron entrypoint. It runs `companion_run.py` for fresh prepare, exits quietly on skip, and starts the stable companion session only when a current event is matched.\n\n## Prepare Stage\n\nCommand:\n\n```bash\npython3 <SKILL_DIR>/scripts/companion_run.py --stage prepare --config <CONFIG> --no-record-pending\n```\n\nCron command:\n\n```bash\npython3 <SKILL_DIR>/scripts/companion_presence_tick.py --config <CONFIG>\n```\n\nPrepare selects the current `day-schedule.md` event by real local time. Events marked `必定发生：是` are life facts, not guaranteed sends.\n\nRequired output fields:\n- `status`\n- `run_id`\n- `life_context`\n- `delivery_contract`\n- `media_contract`\n- `state_commit`\n- `next_step`\n\n`life_context` is structured and must contain:\n- `generated_at`\n- `timezone`\n- `speaker`\n- `today`\n- `event`\n- `reality_check`\n\nOptional:\n- `delivery_mood`\n\nThe tick wrapper passes the prepared contract to the stable companion session when status is ok. That session writes one first-person companion presence story directly from `life_context`. There is no separate render stage and no `render_spec` in the current architecture.\n\nPrepare output must not include duplicated task fields or local-only execution hints:\n- no top-level `primary_goal`\n- no `render_spec`\n- no local runbook fields\n- no private local paths or channel identifiers beyond the configured delivery contract\n\nAgents should not infer delivery ownership from prose. Follow these fields:\n- `delivery_contract.send_in_main_turn = true`: text may be sent in the main turn.\n- `media_contract.kind = event_media`: the current event requires media.\n- `media_contract.async = true`: OpenClaw media generation is asynchronous.\n- `media_contract.tool_name`: the runtime-selected async media generator for the matched media type.\n- `media_contract.completion_event_is_sender = true`: the media task is asynchronous and will produce a native OpenClaw completion event.\n- `media_contract.callback_context.strategy = same_stable_session`: the media completion must return to the same stable companion session that started the media task.\n- `media_contract.callback_context.requires_original_session_context = true`: the completion turn relies on that stable companion session context to keep the original delivery contract available.\n- `state_commit.when`: defines when pacing state can be committed.\n\nThe presence agent must send text through `companion_presence_tick.py --send-story --story-stdin`, not through the runtime `message(action=\"send\")` tool. `--send-story` reloads the saved contract from the dispatch lock, sends the text presence story with the explicit `delivery_contract`, and runs `state_commit.command` only after visible text delivery succeeds.\n\n`life_context.event.media_info` may describe a concrete photo, audio, video, or similar media artifact for the matched event. If present, the presence agent calls the selected async media generation path only after `--send-story` succeeds. The wrapper also starts `companion_presence_tick.py --watch-recent-media-task` for the stable session; that helper finds the next media task created after launch, waits for generated media paths, and sends with the explicit `delivery_contract`. If the native media completion later returns to the same stable companion session, it may only use `companion_presence_tick.py --send-media` as a fallback and must not run `state_commit.command` again.\n\n中文说明：正文主路径是固定 `--send-story` 入口，媒体补发主路径是 wrapper 自动启动的 `--watch-recent-media-task`。两者都不能让模型自己判断 current/original chat；completion 兜底也只能调用固定 `--send-media` 入口。\n\nFor both media events and text-only events, `state_commit.when = after_text_send`. Media success is a follow-up enhancement, not the event completion gate.\n\n`media_contract` stays intentionally generic. It must not carry local runbook paths, private workspace details, or user-specific media instructions.\n\nUse `assets/turn-contract.schema.json` for machine validation.\n\nFile v2.1.9:references/first-time-setup.md\n\n# First-Time Setup Guide\n\nUse this when a user is configuring the skill for the first time or rebuilding it from scratch.\n\nGoal:\n- collect only the minimum decisions the user must make\n- keep delivery fields correct\n- generate `character-profile.md` and `day-schedule.md`\n- wire `companion-presence` without turning onboarding into prompt-writing\n- treat user-defined fixed content as life anchors, not guaranteed sends\n\nIf you need literal onboarding wording, use [agent-first-time-qa-template.md](./agent-first-time-qa-template.md).\n\n## First-Time Setup Order\n\nFollow this order. Do not skip ahead to polishing wording.\n\n1. Confirm the proactive delivery destination.\n2. Decide whether owner info should be imported from `USER.md`, customized manually, or skipped.\n3. Capture and write the companion's `character-profile.md`.\n4. Ask whether the user wants any `必定发生：是` life anchors.\n5. Write and materialize `config.local.json`.\n6. Generate the first `day-schedule.md` with 3-5 ordinary events plus configured required events.\n7. Create or update `companion-build-day-schedule` and `companion-presence`.\n8. Run validation and one controlled user-visible verification.\n\n## What The User Must Decide\n\nAsk for or infer only these fields first:\n\n| Question | Destination | Required? | Notes |\n| --- | --- | --- | --- |\n| Which channel should proactive messages use? | `delivery.channel` | yes | Example: direct message or another configured OpenClaw channel |\n| What exact recipient id should delivery use? | `delivery.owner_target` | yes | Use the real target id, not the current UI label |\n| Which sending account should be used? | `delivery.account` | channel-dependent | Required on channels that need a specific sender account |\n| Import owner info from `USER.md` or customize manually? | `owner_profile` | recommended | Import only stable identity fields; never prompt with private channel/account ids |\n| How old or what life stage is she? | `character-profile.md` | strongly recommended | Example: freshman, early-career, creator |\n| What exactly is her real-world role? | `character-profile.md` | strongly recommended | Example: design intern, game content creator |\n| Which city or district does she live around? | `character-profile.md` | strongly recommended | City alone is often too coarse for believable planning |\n| What interests and entertainment does she naturally follow? | `character-profile.md` | strongly recommended | Used for daily reality anchors and local life texture |\n| Any fixed things that should always enter her day? | `life_schedule.day_schedule.required_events` | optional | These become `必定发生：是` events, not guaranteed messages |\n| Quiet hours? | `schedule.quiet_hours_start` / `schedule.quiet_hours_end` | yes | Default is fine when the user has no preference |\n\nDo not ask for advanced style tuning, derived profile fields, low-level life-schedule internals, or legacy visible cron modes during the first pass.\n\n## Owner Profile Rule\n\nKeep owner information light. The setup only needs enough to distinguish owner from companion:\n- `source`: `user_md`, `manual`, or `none`\n- `preferred_name`\n- `pronouns`\n- `location`\n- `timezone`\n- optional `identity_summary`\n- optional `not_assumptions`\n\nWhen `USER.md` exists, offer import as the recommended path. Import only stable identity fields and do not copy routing identifiers, direct-chat IDs, account IDs, or session keys into prompt-facing context.\n\n## Character Profile Detail Rule\n\nIf `life_schedule.enabled` is true or the user explicitly wants stronger 活人感, do not stop at a thin persona like \"college student in a city\".\n\nAt minimum, capture:\n- life stage or age band\n- concrete school, work, creator, or daily identity\n- city plus a more local area when known\n- interests plus the kinds of entertainment and content she actually follows\n\nWrite these into `character-profile.md`, not into `config.local.json -> persona`.\n\n## Required Event Anchors\n\nRequired events are life facts. They make the daily schedule include something the user cares about, but they do not force a message to be sent.\n\nFor each anchor, capture:\n- time window or preferred time\n- title\n- duration\n- scene\n- what she is doing\n- what can be naturally mentioned\n- owner interaction entry\n- what not to write it as\n\nStore anchors in `life_schedule.day_schedule.required_events`. The daily builder turns them into `day-schedule.md` events with `必定发生：是`.\n\n## Recommended Starter Defaults\n\nUse these when the user says \"先给我一套能跑的\" or has no strong preference:\n\n- daily schedule builder: `10 7 * * *`, isolated, no delivery\n- presence cron: hourly, isolated cron session that calls `companion_presence_tick.py`; the wrapper starts the stable companion runtime session only after a matched event\n- quiet hours: `01:00` to `08:00`\n- required events: none unless the user names one\n\nThe old four-slot content cron setup is deprecated. If the user asks for a fixed daily habit, model it as a required event anchor first.\n\n## Materialized Config Gate\n\nAfter writing `config.local.json`, verify it is not just a copied example.\n\nRequired before cron creation:\n- no placeholder values like `<REQUIRED_CHANNEL>` or `<RUNTIME_SPECIFIC>`\n- actual `character_profile_path`\n- actual `runtime.workspace_root`\n- actual `runtime.sessions_store_path`\n- actual `runtime.state_file`\n- actual `runtime.healthcheck_command`\n- actual `life_schedule` state paths when enabled\n- default `behavior.emotion_thresholds` if the user did not customize them\n\nFor rebuilds or scripted setup, use the migration helper to materialize defaults:\n\n```bash\npython3 scripts/migrate_config.py --config <CONFIG> --owner-source user_md --write\n```\n\nUse `--owner-source manual` or `--owner-source none` when the user chooses those paths.\n\n## Markdown Life Text Initialization\n\nWhen private life is enabled, initialize the Markdown files before creating presence cron. Do not leave the first real run to invent life context from an empty state directory.\n\nCreate or refresh these files in order:\n\n1. `character-profile.md`\n2. `day-schedule.md`\n\nFor `day-schedule.md`, derive today's 3-5 concrete ordinary events directly from the character profile, today's date, public search materials, recent life log, and configured required events. Before ordinary events are written, explicitly run web search with the standard 4-5 keyword mix: one city/weather keyword, one local area/school/workplace/community keyword, one identity/occupation keyword, and one or two interest keywords. Add required events into the same `## 4. 日程事件` section as `必定发生：是`.\n\nEach event must have:\n- `HH:mm - 事件标题`\n- `必定发生：是/否`\n- `执行时间`\n- scene, action, mood, natural mention, interaction entry, media info, and avoid rule\n- no quiet-hour overlap\n- no overlapping event windows\n- no duplicate event types in the same day, including duplicates with configured required events\n- every searched category is consumed by at least one normal character behavior event rather than becoming a standalone news/material-browsing event\n\nRun the Markdown validators immediately:\n\n```bash\npython3 scripts/validate_character_profile.py --profile <CHARACTER_PROFILE>\npython3 scripts/validate_day_schedule.py --config <CONFIG> --path <DAY_SCHEDULE>\n```\n\nIf validation fails, fix the Markdown file itself before creating cron jobs.\n\n## Delivery Pitfalls To Prevent\n\nBefore creating cron jobs, make sure the onboarding agent has stated these rules:\n\n- proactive outbound delivery follows the local config delivery block\n- `companion-presence` runs in an isolated cron session, not the owner conversation\n- `companion_presence_tick.py` starts the stable companion runtime session only after fresh prepare returns a matched event\n- final companion text is sent through the prepared delivery contract\n- state is committed only after visible text delivery succeeds\n- media turns commit state after visible text delivery, then start OpenClaw async media generation; the native completion returns to the same stable companion session and only sends media\n- required events are not guaranteed sends\n\n## First Verification Gate\n\nFor a fresh install, do one controlled real run after setup:\n\n1. Run `python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG> --skip-smoke`.\n2. Ensure `day-schedule.md` has a current event or create a temporary validated test schedule.\n3. Run `companion_run.py --stage prepare --config <CONFIG> --no-record-pending`.\n4. Confirm the prepare contract has `life_context`, `delivery_contract`, `media_contract`, and `state_commit`; it must not have `render_spec`, `media_task_record_contract`, or top-level `primary_goal`.\n5. Send one first-person companion presence story to the intended owner target through `companion_presence_tick.py --send-story`.\n6. Confirm `--send-story` commits state only after text delivery succeeds. For media events, start async media after the commit; do not wait for media success to mark the event complete.\n\nIf delivery fails, fix routing first. Do not keep polishing personality copy while the send path is still untrusted.\n\nFile v2.1.9:references/presence-integration.md\n\n# Presence Integration\n\nUse this when wiring the skill into OpenClaw jobs or sessions.\n\n当前默认主动链路只有 `companion-presence`。cron 运行在 isolated session 中，只调用 `scripts/companion_presence_tick.py`；该 wrapper 先确定性运行 `scripts/companion_run.py --stage prepare --no-record-pending`，读取当前 `day-schedule.md` 事件。未命中时静默退出，命中后才把准备好的合同交给稳定 companion session 发送 presence story；如果是媒体事件，wrapper 还会启动后台 recent-media watcher 来按显式合同投递生成媒体。OpenClaw completion 可能仍回到同一个稳定 companion session，但不再是正确渠道投递主路径。事实连续性仍只来自本地状态文件。\n\n## Runtime Pieces\n\nRequired local pieces:\n- `scripts/companion_run.py`\n- materialized `config.local.json`\n- `state/character-profile.md`\n- `state/day-schedule.md`\n- `state/companion-state.json`\n- optional continuity file `state/life-log.jsonl`\n\n`config.local.json` may contain real local paths and delivery ids. Do not copy those values into publishable docs, examples, cron templates, or user-visible companion text.\n\n## Presence Cron Shape\n\nRecommended job:\n\n```json\n{\n  \"name\": \"companion-presence\",\n  \"description\": \"Owner-only cyber girlfriend presence cron\",\n  \"schedule\": {\n    \"kind\": \"cron\",\n    \"expr\": \"0 * * * *\",\n    \"tz\": \"Asia/Shanghai\"\n  },\n  \"sessionTarget\": \"isolated\",\n  \"payload\": {\n    \"kind\": \"agentTurn\",\n    \"message\": \"<PRESENCE_SINGLE_RUNNER_TEMPLATE>\"\n  },\n  \"delivery\": {\n    \"mode\": \"none\"\n  },\n  \"enabled\": true\n}\n```\n\nThe payload should do only this:\n\n```text\n1. 第一个也是唯一业务动作是触发 companion_presence_tick.py --config <CONFIG>。\n2. 如果脚本输出 skip、agent_enqueued、notification_sent 或其他已处理状态，都只回复 NO_REPLY。\n3. 不要自己读取 day-schedule.md，不要自己判断当前事件，也不要自己处理消息发送或媒体生成。\n```\n\nDo not pass `--event-time` in the live cron. Presence reads the real current local time.\n\n## Message Rules\n\n- Final text must be first person from the companion's perspective and must fit the cyber-girlfriend persona.\n- Unless the matched required event defines a special structure, write one complete, rich, specific event story.\n- Include the companion's current emotion and inner thought.\n- Write at least 160 Chinese characters; before sending, self-check the final text and expand with event details or inner thought if it is shorter.\n- If the current event contains an interaction entry for the user, express it naturally and do not omit it.\n- Use the current event in `life_context`, not stale memories or unrelated technical incidents.\n- After a matched event is selected, extract 2-4 public, non-sensitive keywords from that current event and do a real public-web search. Use at most 1-2 small details only to make the same event feel more concrete and real.\n- If search is temporarily unavailable, noisy, or adds nothing useful, still treat the search step as mandatory and then fall back to the original event details without mentioning search failure in the final message.\n- Do not mention scripts, JSON, cron, tools, models, routing, status values, step names, or diagnostics.\n- Keep owner and companion separate; never project the companion's school, room, friends, schedule, or private life onto the owner.\n- Public-web search is only a light grounding layer for the matched current event; never let it replace the current event or turn the message into a news summary.\n\n## Delivery Rules\n\n- External delivery must use explicit channel/account/target from `delivery_contract`.\n- `companion-presence` runs in an isolated cron session and only calls the deterministic wrapper.\n- The wrapper starts the stable custom companion runtime session such as `session:companion-runtime` only after prepare returns `status = \"ok\"`.\n- Presence sends final text through the prepared delivery contract.\n- State commits only after confirmed visible delivery.\n- A second tick in the same event should skip because the event was already sent.\n\n## Media Callback Rules\n\nFor media events, the text presence turn is allowed to end before media generation completes. The default path uses a deterministic wrapper-launched watcher for delivery while still letting the stable companion session start OpenClaw media generation:\n\n1. Write the text presence story first, then send it through `companion_presence_tick.py --send-story`.\n2. Let `--send-story` send with the explicit `delivery_contract` and run `state_commit.command` only after visible text delivery succeeds.\n3. Use `life_context.event.media_info` to start the matching async media generation defined by `media_contract` only after `--send-story` succeeds.\n4. The wrapper starts `companion_presence_tick.py --watch-recent-media-task` in the background for that stable session. The watcher finds the new media task by stable session key and wrapper launch timestamp, waits for the generated path, and sends media explicitly.\n5. Do not run `state_commit.command` again in the media completion turn.\n\nThe runner contract exposes `media_contract.callback_context.strategy = same_stable_session` and `requires_original_session_context = true` to make the stable-session generation context explicit. The native completion turn may still arrive, but it must not use the runtime's current/original chat as the media target; if it is used as a fallback, it must call `--send-media` with the generated path or URL. `--watch-media-task` remains available when a concrete task id is already known.\n\n中文说明：文本发送由固定 `--send-story` 入口处理；媒体补发由 wrapper 后台 `--watch-recent-media-task` 自动处理，不依赖模型在媒体工具返回后继续执行。原生 completion 即使回来，也不能把 current chat 当作目标。\n\n## Verification\n\nBefore declaring setup or upgrade complete:\n\n1. Run `python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG> --skip-smoke`.\n2. Ensure `day-schedule.md` has a current event, or create a temporary validated schedule for testing.\n3. Run `python3 scripts/companion_presence_tick.py --config <CONFIG> --dry-run`.\n4. Confirm dry-run output is `would_start_agent` for a matched event or `skip` when no event is active.\n5. Confirm prepare output does not expose private paths, channel ids, account ids, session ids, `render_spec`, or top-level `primary_goal`.\n6. Run one controlled presence delivery.\n7. Confirm the owner saw exactly one message.\n8. Confirm the next tick in the same event returns a quiet skip.\n9. For a media event, run a controlled watcher test: confirm text sends first, state commits after text delivery, media generation starts, the wrapper records a background watcher pid/log, watcher log ends with `media_task_sent`, and any native completion does not become the only correct-channel delivery path.\n\nFile v2.1.9:references/private-life-cron-templates.md\n\n# Private Life Cron Templates\n\nUse this when the companion's private-life layer needs to run on OpenClaw.\n\nCurrent job set:\n1. `companion-build-day-schedule` compiles today's `day-schedule.md`.\n2. `companion-presence` reads the current day-schedule event and sends a light companion message through the fixed `--send-story` delivery entrypoint when allowed.\n\nOld four-slot content cron jobs are deprecated. Fixed user-desired content should usually become `life_schedule.day_schedule.required_events`, then appear in `day-schedule.md` as `必定发生：是`.\n\nPair this with:\n- [private-life-prompt-templates.md](./private-life-prompt-templates.md)\n- [presence-integration.md](./presence-integration.md)\n- [private-life-layer.md](./private-life-layer.md)\n\n## Design Rules\n\n- Do not store dynamic day content inside `config.local.json`.\n- `config.local.json` only stores paths, switches, delivery, and stable policy.\n- `build-day-schedule` is a context-generation job, not a user-facing message job.\n- Context-generation jobs should be quiet by default: `sessionTarget: isolated`, `delivery.mode: none`.\n- `companion-presence` should run from an isolated cron session and call `scripts/companion_presence_tick.py`. The wrapper performs fresh prepare first and only starts the stable custom companion runtime session when a current event is actually matched.\n- `companion-presence` sends final text through the prepared delivery contract and commits state only after visible text delivery succeeds.\n- Media events send text first, commit event state after visible text delivery, start the OpenClaw async media tool, then let the native completion in the same stable companion session only send media.\n- Required events are life anchors, not guaranteed messages.\n- Before generating ordinary events, `companion-build-day-schedule` must explicitly do real public-web research. First extract a keyword pool from `character_profile`: current city or weather area, local area/school/workplace/community, identity or occupation, and interests. Then search 4-5 public, non-sensitive keywords with this exact mix: 1 city/weather keyword, 1 area/school/workplace/community keyword, 1 identity/occupation keyword, and 1-2 interest keywords. Do not treat model memory or unsourced prior knowledge as search. Every searched category must be consumed by at least one event, and the consumption must appear in that event's scene, action, natural mention, or avoid rule, not only in the run summary. Use only a few concrete public details as background texture inside normal character behaviors; do not create a standalone browsing-news, reading-material, or public-info event just to use search results. Do not search owner identity, private relationship facts, account ids, channel ids, local paths, secrets, or private config/state content.\n\n## Template: `companion-build-day-schedule`\n\nSuggested time: `10 7 * * *`\n\n```json\n{\n  \"name\": \"companion-build-day-schedule\",\n  \"description\": \"Compile companion daily event schedule\",\n  \"schedule\": {\n    \"kind\": \"cron\",\n    \"expr\": \"10 7 * * *\",\n    \"tz\": \"<TZ>\"\n  },\n  \"sessionTarget\": \"isolated\",\n  \"payload\": {\n    \"kind\": \"agentTurn\",\n    \"message\": \"在工作区 `<SKILL_DIR>` 生成/更新今天的 `day-schedule.md`，把用户定义的必定发生生活锚点并入当天日程。目标：生成一份可被 `companion-presence` 按当前时间命中的 Markdown 日程，每个事件都必须可采样、可讲述、可校验。\\n\\n执行流程：\\n1. 只读取 `<CONFIG_PATH>`、`references/private-life-prompt-templates.md`、`assets/day-schedule.example.md`、`state/character-profile.md`，以及存在时最近几条 `state/life-log.jsonl`；不要扫描整个目录或搜索 `tests/`。\\n2. 从配置装配 `owner_profile`、`relationship`、`timezone`、`required_events`、`quiet_hours`、`schedule_path`；从角色档案装配 `character_profile`；按时区计算 `today_date`，life log 不存在时按空处理。\\n3. 生成事件前，必须先做真实联网搜索。先从 `character_profile` 提取关键词池：当前城市或天气区域、区域/学校/工作地点/社区、身份/职业、兴趣爱好；再搜索 4-5 个公开、非敏感关键词，固定配比为：城市/天气 1 个、区域/学校/工作地点/社区 1 个、身份/职业 1 个、兴趣爱好 1-2 个。不能用模型记忆、已有知识或无来源猜测代替联网搜索。每个搜索类别都必须至少被一个事件消费，消费痕迹必须落在该事件的 `场景`、`正在做什么`、`可自然提到` 或 `不要写成` 中，不能只出现在运行摘要里。只提炼少量具体公共细节作为背景质感，融入学习、工作、吃饭、整理、出门、娱乐、运动、创作等符合人物行为的普通事件里；不要为了使用搜索结果单独生成“浏览新闻/翻公开素材/看资料”事件，也不要把日程写成新闻清单；不搜索 owner 身份、私人关系、账号、频道、会话、本机路径、密钥或私密配置/状态。\\n4. 读取 `DAY_SCHEDULE_PROMPT`，参考 `assets/day-schedule.example.md`，先生成 3-5 个普通事件并标注 `必定发生：否`；再把每个 `required_events` 写入同一个 `## 4. 日程事件` 区块并标注 `必定发生：是`，它们不计入普通事件额度。\\n5. 检查并修正：事件窗口不能重叠，不能落入静谧时段，必须覆盖至少一个整点 presence 采样点；普通事件之间不得重复类型，普通事件也不得和 required events 重复类型或内容。\\n6. 检查媒体字段：`媒体信息` 默认留空；只有确实需要生成照片、音频、音乐、视频或类似媒体文件时才填写；不要写“不生成媒体”之类备注；若 required event 提供 `media_hint`，必须转写进对应必定事件。\\n7. 将最终 Markdown 写入 `<DAY_SCHEDULE>`，只运行 `python3 <SKILL_DIR>/scripts/validate_day_schedule.py --config <CONFIG_PATH> --path <DAY_SCHEDULE>`；失败就修 Markdown 并重跑同一命令，直到通过或明确说明阻塞。\\n8. 校验通过后，只输出简短中文摘要：普通日常事件数量、必定发生事件数量、今日主场景、按类别列出的联网搜索关键词/来源概况和对应消费事件、1 条避免重复项和媒体事件数量。\\n\\n输出与事件细节要求：\\n- 输出和写入 Markdown，不要写 JSON；不要生成独立任务区块，所有生活事件都在 `## 4. 日程事件` 下。\\n- 最终 `day-schedule.md` 只保留角色日程内容，不要把生成约束、输出要求、校验规则、执行流程或提示词说明写进文件。\\n- 每个事件标题必须是 `HH:mm - 事件标题`，标题只写核心动作。\\n- 每个事件必须包含：`必定发生：是/否`、`执行时间`、`场景`、`正在做什么`、`情绪/状态`、`可自然提到`、`用户互动入口`、`媒体信息`、`不要写成`。\\n- `正在做什么` 必须展开为 2-3 个分句，写清事件对象是什么、对象里有什么可辨认内容、她正在处理哪一步或按什么标准做取舍。\\n- 先判断事件对象类型再补细节：资料/文件/课程/工作项写主题、页段、问题点或收尾标准；物品/空间/行李/穿搭写 2-4 个具体物件和摆放、挑选或清理动作；人际/协作/服务写对方关系、对话焦点和回应边界；兴趣/内容/活动/运动/创作写具体名称、片段、动作、练习点、评价标准或选择理由；饮食/通勤/天气/采购写地点、物品、路线、环境影响和一个小取舍。\\n- 不要把“那件事”“那个东西”“最后一页”“几个片段”“一些资料”“几句话”“那边”当作最终细节；出现这类指代时后面必须紧跟具体内容或可感知特征。\\n- `场景` 要写地点 + 身边物品或环境状态，`可自然提到` 要承接事件里的具体对象。\\n- 允许戏剧性/反差性，但不能灾难化、危险、病痛、家庭伦理、极端情绪或失控冲突。\\n- 用户互动入口可以为空；如果填写，必须是自然轻量的互动，不要每个事件都围绕用户。\\n- owner_profile 只用于边界，不要把 companion 的身份、经历、日常素材写成 owner 的经历。\\n- 可选缓存文件不存在时按空处理；但联网搜索必须真实发生，搜索无结果时在摘要说明无有效结果，再降级生成。\\n- 不要出现或写入本机路径、渠道、账号、脚本、JSON 合同、cron、系统、模型、工具或运行步骤等用户可见内部词。\\n- 不给主人发消息。\"\n  },\n  \"delivery\": {\n    \"mode\": \"none\"\n  },\n  \"enabled\": true\n}\n```\n\n## Template: `companion-presence`\n\nSuggested time: hourly, for example `0 * * * *`.\n\n`companion-presence` should run in an isolated cron session, keep the owner conversation clean, and do only one job: call the deterministic tick wrapper. The wrapper exits quietly on `skip` and starts the stable companion runtime session only when prepare returns `status = \"ok\"`.\n\nCore prompt shape:\n\n```text\n每次执行只做下面动作：\n\n1. 第一个也是唯一业务动作是触发：\n   python3 <SKILL_DIR>/scripts/companion_presence_tick.py --config <CONFIG_PATH>\n2. 如果脚本输出 `skip`、`agent_enqueued`、`notification_sent` 或其他已处理状态，都只回复 NO_REPLY。\n3. 不要自己读取 `day-schedule.md`，不要自己判断当前事件，也不要自己处理消息发送或媒体生成。\n```\n\nPresence cron does not use `--event-time`. It only reads the current real-time event from `day-schedule.md`.\n\n## Optional Midday Refresh\n\nUse only if the user wants same-day adaptation to weather/news changes.\n\nSuggested time: `40 12 * * *`.\n\nSame as `companion-build-day-schedule`, but the prompt should preserve the morning-built schedule unless reality meaningfully changed.\n\n## Validation Checklist\n\nAfter wiring the private-life cron layer:\n\n1. `companion-build-day-schedule` writes valid `day-schedule.md`.\n2. `day-schedule.md` has 3-5 ordinary events and any required events marked `必定发生：是`.\n3. `companion_run.py --stage prepare --no-record-pending` can emit `life_context` when a current event is active.\n4. `companion-presence` skips quietly when no event is active.\n5. Missing or stale `day-schedule.md` is recorded in state; after repeated failures, presence can send one soft maintenance notice.\n6. After one successful send, `life-log.jsonl` gains a valid line and state advances only after delivery.\n\nFile v2.1.9:references/private-life-layer.md\n\n# Private Life Layer\n\nThis reference defines the companion's own lived-context layer.\n\nUse it when implementing or updating:\n- daily schedule compilation\n- required life event anchors\n- continuity logging across proactive messages\n- presence cron context injection\n\n## Goal\n\nGive the companion a believable private life without turning the system into heavy roleplay.\n\nThe companion should feel like:\n- she already had a day before messaging the owner\n- she has continuity across days\n- she lives in the same real-world calendar/timezone as the owner\n- she can lightly react to holidays, weather, and topical events\n- her daily rhythm is constrained by who she is in the real world\n\nThe companion should not feel like:\n- an improv soap opera\n- a manipulative relationship sim\n- a minute-by-minute scheduler\n- a spammy diary bot\n- a mirror that projects her school/work/private life onto the owner\n\n## Three Layers\n\nKeep owner identity as a separate config boundary. The private-life layer belongs to the companion; school, dorm, classmate, coursework, workplace, commute, and friend details must not be assumed to describe the owner unless the owner profile explicitly says so.\n\n### 1. Character Profile Layer\n\nThis is who she is.\n\nThe file is `character-profile.md`, usually at `./state/character-profile.md`.\n\nIt owns:\n- name and owner-facing nickname\n- age / life stage\n- identity role\n- city / district when relevant\n- institution, workplace, creative background, or focus area\n- personality, interests, entertainment tastes, and relationship expression\n- speech habits and safety boundaries\n\n`config.local.json -> persona` is deprecated. Older JSON persona values may be used as migration input, but new installs should keep the companion's core character in Markdown.\n\n### 2. Daily Schedule Layer\n\nThis is what is likely true today.\n\nIt should include:\n- day type\n- weather / season hints\n- 3-5 ordinary `HH:mm - event title` schedule events outside configured quiet hours\n- configured required events from `life_schedule.day_schedule.required_events`\n- `必定发生：否` for ordinary events and `必定发生：是` for required events\n- event scene, current activity, emotional state, mentionable detail, owner interaction entry, and avoid guidance\n- continuity notes from recent interactions\n\nRequired events do not count toward the 3-5 ordinary event quota. They are life anchors, not guaranteed sends.\n\nThe daily schedule should not include quiet-hour policy itself. Quiet hours are read from local skill config.\n\n### 3. Continuity Layer\n\nEvery successful proactive message may contribute a few `life_claims`.\n\nExamples:\n- \"今天外面很闷\"\n- \"刚从便利店回来\"\n- \"晚上可能会慢慢收尾\"\n\nThese claims should be appended to `life-log.jsonl` and used to prevent repetition and contradictions.\n\n## Runtime Use Pattern\n\nRecommended flow:\n\n1. daily planning generates `day-schedule.md`\n2. `companion-presence` runs in an isolated cron session and calls `companion_presence_tick.py`\n3. the wrapper runs fresh prepare and selects the current real-time event from `day-schedule.md`\n4. only matched events start the stable companion runtime session\n5. the runtime writes one first-person companion message\n6. the runtime calls `companion_presence_tick.py --send-story`; that fixed entrypoint sends through the explicit delivery contract and commits state after successful text delivery\n7. media events then start OpenClaw async media generation, and native completion in the same stable companion session only provides fallback media paths\n\nPresence cron does not manually assemble the life layer and does not pass `--event-time`.\n\n## Realism Rules\n\n- Prefer light slices of life over full status reports.\n- Mention at most 1-2 life details per message by default.\n- Do not make every message about the owner.\n- Do not make every message about her own life either.\n- Blend her life context with owner context naturally.\n- Avoid high-drama events unless the user explicitly wants that mode.\n\n## What Daily Schedule Should Produce\n\nGood daily output:\n- 3-5 concrete but low-drama ordinary events\n- additional required events from config anchors when present\n- `HH:mm` event headings outside configured quiet hours\n- every event has `必定发生：是/否`, `执行时间`, and `媒体信息`\n- a few mentionable details attached to real event scenes\n- no duplicate event type in one day, including ordinary events duplicating required anchors\n\nBad daily output:\n- fake certainty about exact actions\n- a huge narrative paragraph\n- a mechanical punch-clock timetable\n- too many named side characters\n- contradictions with yesterday's sent message\n\n## Minimal Injection Contract\n\nPresence cron calls `companion_presence_tick.py`; the wrapper calls `companion_run.py --stage prepare` so the runner selects the matching current event from `day-schedule.md`, then emits structured `life_context`.\n\nEvents marked `必定发生：是` are life anchors. If presence cron does not run inside that event window, the system does not backfill a forced message.\n\nThe final message should feel like:\n- she already existed before this message\n- she is sharing a small slice, not narrating a report\n\nFile v2.1.9:references/private-life-prompt-templates.md\n\n# Private Life Prompt Templates\n\nUse these templates when implementing the companion's private-life layer.\n\nThese are not user-facing messages. They are planner/compiler prompts for generating:\n- `day-schedule.md`\n- optional lightweight life-claim extraction for `life-log.jsonl`\n\nOld `month-plan.json` and `day-context.json` artifacts are migration inputs only. Do not use them as the main runtime output for new installs.\n\n## Prompting Principles\n\n- specific lived events > generic routine labels\n- controlled drama / contrast is allowed when it fits the persona\n- grounded ordinary life > melodramatic fiction\n- concrete daily rhythm > long story arcs\n- current event window > generic slice-of-life pool\n- real-world anchors > free-floating vibes\n- character-profile-specific routine > generic \"young girl ambience\"\n- modern everyday texture > elaborate backstory\n- one or two small details > long narrative paragraphs\n\nDo not generate:\n- serious illness by default\n- family conflict by default\n- manipulative guilt hooks aimed at the owner\n- disaster, danger, legal trouble, or extreme emotional collapse by default\n- exact fake appointments unless the user explicitly wants that mode\n- private channel ids, account ids, local paths, session keys, or user identity details\n\n## Template: `DAY_SCHEDULE_PROMPT`\n\n```text\n你要为 companion 生成“今天的具体日程事件”，输出必须是 Markdown，不要输出解释。\n\n目标：\n- 从角色档案、今日现实信号、required events 和 recent life log 生成 3-5 个可信普通事件\n- 把用户初始化时确认的必定生活锚点并入同一套日程事件\n- 让 heartbeat 只在当前事件窗口内引用她正在做的事\n- 避免和最近 life-log 冲突\n\n输入信息：\n- character_profile：<CHARACTER_PROFILE_MD>\n- owner_profile：<OWNER_PROFILE_JSON>\n- relationship：<RELATIONSHIP_JSON>\n- timezone：<TIMEZONE>\n- city：<CITY>\n- today_date：<TODAY_DATE>\n- weather_hint：<WEATHER_HINT>\n- calendar_hint：<CALENDAR_HINT>\n- topical_hint：<TOPICAL_HINT>\n- public_search_materials：<PUBLIC_SEARCH_MATERIALS>\n- recent_life_log：<RECENT_LIFE_LOG>\n- required_events：<USER_DEFINED_REQUIRED_EVENTS>\n\n生成要求：\n1. 生成普通事件前必须使用 `public_search_materials`。该素材必须来自真实联网搜索：从 `character_profile` 提取当前城市或天气区域、区域/学校/工作地点/社区、身份/职业、兴趣爱好，按城市/天气 1 个、区域/学校/工作地点/社区 1 个、身份/职业 1 个、兴趣爱好 1-2 个的配比搜索 4-5 个公开、非敏感关键词。模型记忆、已有知识或无来源猜测不能算作联网搜索。\n2. 每个联网搜索类别都至少被一个事件消费，消费痕迹必须写进事件的 `场景`、`正在做什么`、`可自然提到` 或 `不要写成`；不要只在摘要里列出搜索词。\n3. 搜索素材只作为普通生活行为的背景质感，融入学习、工作、吃饭、整理、出门、娱乐、运动、创作等符合人物身份的事件里；不要为了使用搜索结果单独生成“浏览新闻/翻公开素材/看资料”事件，也不要把日程写成新闻清单。\n4. 不搜索 owner 身份、私人关系事实、账号、频道、会话、本机路径、密钥或 config/state 私密内容。\n5. 每天生成 3-5 个具体日常事件，格式必须是 `HH:mm - 事件标题`。\n6. 每个事件必须包含这些字段：\n   - 必定发生：`是` 或 `否`\n   - 执行时间：例如 `35 分钟`、`1 小时 20 分钟`\n   - 场景\n   - 正在做什么\n   - 情绪/状态\n   - 可自然提到\n   - 用户互动入口\n   - 媒体信息\n   - 不要写成\n7. 对 `required_events` 中的每一条生成一个 `必定发生：是` 事件，放在同一个 `## 4. 日程事件` 下。\n8. `必定发生：是` 事件不计入 3-5 个普通日常事件额度。\n9. 一天内不要生成重复类型的事件；普通事件不要和 `required_events` 生成的必定发生事件重复。\n10. `必定发生` 字段用于区分初始化生活锚点和普通事件，不能删除，也不能把普通事件误标成 `是`。\n11. `媒体信息` 默认留空；如果事件涉及拍照、唱歌、录音、视频或类似媒体内容，必须写清 agent 命中事件时应生成的具体媒体文件内容。\n   - 如果 `required_events` 提供了 `media_hint`，必须把它转写进该必定事件的 `媒体信息` 字段；若 `media_hint` 要求“根据当天主要日程生成”，则媒体画面必须取材于当天普通事件、今日背景和连续性记录。\n   - 照片类媒体默认写成自然生活照/陪伴照片，不要默认写成自拍、镜子自拍或手持前置镜头；只有 required event 明确要求，或当前场景/动作天然需要自拍视角时，才把拍摄方式写成自拍。\n12. 每个事件的细节必须展开到“能直接写成一段 presence story”的程度：\n   - `事件标题` 只写核心动作，但 `正在做什么` 必须用 2-3 个分句补清楚：事件对象是什么、对象里有什么可辨认内容、她正在处理哪一步或按什么标准做取舍。\n   - 先判断事件对象类型，再补对应细节：资料/文件/课程/工作项要写主题、页段、问题点或收尾标准；物品/空间/行李/穿搭要写 2-4 个具体物件和摆放、挑选或清理动作；人际/协作/服务场景要写对方关系、对话焦点和她的回应边界；兴趣/内容/活动/运动/创作要写具体名称、片段、动作、练习点、评价标准或当下选择理由；饮食/通勤/天气/采购要写具体地点、物品、路线、环境影响和一个小取舍。\n   - 如果无法确定某个真实名称或精确信息，可以用可信的概括名补足到可感知层级，例如“蓝色封面的项目笔记本”“社区健身房靠窗跑步机”“周报里客户反馈那一段”，不要伪造私密编号、真实账号或敏感身份。\n13. 不要把“那件事”“那个东西”“最后一页”“几个片段”“一些资料”“几句话”“那边”当作最终细节；出现这类指代时，后面必须紧跟具体内容或可感知特征。\n14. `场景` 要写到地点 + 身边物品或环境状态，`可自然提到` 要承接事件里的具体对象，不要只写心情总结。\n15. 事件可以有戏剧性或反差性，例如计划被天气打断、临时找不到东西、被同学一句话逗到、想偷懒但又把一件小事做完；不要升级成事故、病痛、家庭伦理、危险或极端情绪。\n16. 不要写静谧时段；静谧时段从本地配置读取。\n17. presence cron 每小时整点采样；每个事件的时间窗口必须覆盖至少一个整点，例如 `12:30 + 45 分钟` 覆盖 `13:00`，`16:20 + 50 分钟` 覆盖 `17:00`。\n18. 不要把全天写成等用户、想用户或为了给主人发消息。\n19. recent_life_log 中刚说过的生活细节不要重复。\n20. owner_profile 只用于身份边界；不要把 companion 的事件投射成 owner 的经历。\n21. 不要出现脚本、系统、模型、工具等内部词。\n\n输出格式：\n# 角色日程\n\n## 1. 今日背景\n- 日期：YYYY-MM-DD\n- 城市/时区：...\n- 今日底色：...\n\n## 4. 日程事件\n\n### 08:30 - ...\n- 必定发生：否\n- 执行时间：...\n- 场景：地点 + 身边物品或环境状态\n- 正在做什么：核心动作；具体对象内容；正在处理的步骤/判断标准/收尾动作\n- 情绪/状态：...\n- 可自然提到：承接事件具体对象的一句自然素材\n- 用户互动入口：...\n- 媒体信息：\n- 不要写成：...\n\n### 14:20 - ...\n- 必定发生：是\n- 执行时间：...\n- 场景：...\n- 正在做什么：...\n- 情绪/状态：...\n- 可自然提到：...\n- 用户互动入口：...\n- 媒体信息：\n- 不要写成：...\n\n## 今日边界\n- 避免重复：...\n- 不要提：...\n- 可以轻轻延续：...\n```\n\n---\n\n## Template: `MODE_LIFE_CONTEXT_INJECTION`\n\nUse this as a shared insertion block inside message-producing cron prompts.\n\n```text\n在写最终消息前，使用 `companion_run.py --stage prepare` 输出的 `life_context`。\n\n使用规则：\n1. 只使用 `life_context.event` 中当前命中的事件，不手动重读今日日程或 life-log。\n2. 当前没有命中事件时，按 prepare 合同 skip 或 needs_review 处理，不要临时编造她正在做什么。\n3. 最终文本必须符合赛博女友人设，用 companion 第一人称写。\n4. 除非用户定义 required events 时有特殊结构要求，否则统一写成：第一人称描述完整、丰富、具体的事件故事，融入人物当时的情绪和心理活动。\n5. 文本长度不少于 160 个中文字符；发送前自检，不足时补充当前事件细节或心理活动。\n6. 命中事件后，必须先从当前事件中提取 2-4 个公共关键词联网搜索，例如地点类型、天气线索、公共活动、节目/游戏/版本/商品/话题名称、路线、物品类别或创作材料；只补 1-2 个能让事件更具体、更有现实感的公共细节。\n7. 联网搜索只用于当前事件的轻量补充，不能搜索 owner 身份、私人关系事实、账号、频道、会话、本机路径、密钥或 config/state 里的私密内容；不能把结果写成新闻播报，也不能加入和当前事件无关的信息。\n8. 如果联网能力暂时不可用、结果噪音大或对当前事件没有帮助，就直接使用原事件细节，不要提搜索失败；但不能主动跳过联网搜索步骤。\n9. 如果当前事件里有 `interaction` 或其他和用户互动的部分，一定要合理表述，不能省略。\n10. 写好正文后，必须调用 `companion_presence_tick.py --send-story` 固定入口发送文本；不能直接调用 runtime `message(action=\"send\")`。固定入口会按 `delivery_contract` 显式投递正文，并在文本投递成功后执行 `state_commit.command`，把本次事件标记完成。如果\n\nArchive v2.1.6: 29 files, 105036 bytes\n\nFiles: agents/openai.yaml (324b), assets/character-profile.example.md (4006b), assets/cyber-girlfriend.config.example.json (2668b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (3828b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3161b), CHANGELOG.md (8944b), references/agent-first-time-qa-template.md (4340b), references/configuration.md (9290b), references/contract-schema.md (3322b), references/first-time-setup.md (9118b), references/presence-integration.md (5477b), references/private-life-cron-templates.md (10468b), references/private-life-layer.md (5097b), references/private-life-prompt-templates.md (11488b), references/required-events-and-cron.md (3797b), references/script-contract-v2-migration.md (2231b), references/standard-init-upgrade-flow.md (11316b), scripts/companion_presence_tick.py (19941b), scripts/companion_run.py (79645b), scripts/migrate_config.py (12179b), scripts/validate_character_profile.py (4888b), scripts/validate_day_schedule.py (13859b), scripts/validate_release.py (31115b), skill-card.md (2860b), SKILL.md (9191b), _meta.json (135b)\n\nArchive v2.1.5: 29 files, 102468 bytes\n\nFiles: agents/openai.yaml (324b), assets/character-profile.example.md (4006b), assets/cyber-girlfriend.config.example.json (2668b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (3828b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3161b), CHANGELOG.md (8036b), references/agent-first-time-qa-template.md (4340b), references/configuration.md (9290b), references/contract-schema.md (3322b), references/first-time-setup.md (9118b), references/presence-integration.md (5477b), references/private-life-cron-templates.md (10468b), references/private-life-layer.md (5097b), references/private-life-prompt-templates.md (11488b), references/required-events-and-cron.md (3797b), references/script-contract-v2-migration.md (2231b), references/standard-init-upgrade-flow.md (11316b), scripts/companion_presence_tick.py (12926b), scripts/companion_run.py (79645b), scripts/migrate_config.py (12179b), scripts/validate_character_profile.py (4888b), scripts/validate_day_schedule.py (13859b), scripts/validate_release.py (31069b), skill-card.md (2838b), SKILL.md (8672b), _meta.json (135b)\n\nArchive v2.1.4: 29 files, 102494 bytes\n\nFiles: agents/openai.yaml (324b), assets/character-profile.example.md (4006b), assets/cyber-girlfriend.config.example.json (2668b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (3828b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3161b), CHANGELOG.md (7589b), references/agent-first-time-qa-template.md (4340b), references/configuration.md (9290b), references/contract-schema.md (3343b), references/first-time-setup.md (9121b), references/presence-integration.md (5449b), references/private-life-cron-templates.md (10527b), references/private-life-layer.md (5097b), references/private-life-prompt-templates.md (11547b), references/required-events-and-cron.md (3806b), references/script-contract-v2-migration.md (2231b), references/standard-init-upgrade-flow.md (11310b), scripts/companion_presence_tick.py (12926b), scripts/companion_run.py (79645b), scripts/migrate_config.py (12179b), scripts/validate_character_profile.py (4888b), scripts/validate_day_schedule.py (13859b), scripts/validate_release.py (31069b), skill-card.md (3266b), SKILL.md (8454b), _meta.json (135b)\n\nArchive v2.1.3: 29 files, 100143 bytes\n\nFiles: agents/openai.yaml (324b), assets/character-profile.example.md (4006b), assets/cyber-girlfriend.config.example.json (2668b), assets/cyber-girlfriend.config.schema.json (13954b), assets/day-schedule.example.md (3828b), assets/life-log-entry.schema.json (894b), assets/life-log.example.json (537b), assets/turn-contract.schema.json (3239b), CHANGELOG.md (7251b), references/agent-first-time-qa-template.md (4340b), references/configuration.md (9290b), references/contract-schema.md (3369b), references/first-time-setup.md (9138b), references/presence-integration.md (5466b), references/private-life-cron-templates.md (10544b), references/private-life-layer.md (5097b), references/private-life-prompt-templates.md (11547b), references/required-events-and-cron.md (3806b),...","readmeExcerpt":"Skill: Cyber Girlfriend Owner: kasanuowa Summary: Owner-only proactive companion system Tags: companion:2.0.1, cron:2.0.1, latest:2.2.0, openclaw:2.0.1, persona:2.0.1 Version history: v2.2.0 | 2026-09-03T02:47:04.689Z | user OpenClaw automation compatibility: deterministic exact-argv presence commands, fresh dispatch sessions, explicit-contract WeChat fallback with retryable delivery failures, and lightweight daily-b","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"python3 scripts/validate_release.py --root <SKILL_DIR> --config <CONFIG>"},{"language":"bash","snippet":"python3 scripts/migrate_config.py --config config.local.json --write"},{"language":"bash","snippet":"python3 <SKILL_DIR>/scripts/companion_run.py --stage prepare --config <CONFIG> --no-record-pending"},{"language":"json","snippet":"{\n  \"kind\": \"command\",\n  \"argv\": [\n    \"python3\",\n    \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n    \"--config\",\n    \"<CONFIG>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n  \"outputMaxBytes\": 65536\n}"},{"language":"bash","snippet":"python3 scripts/migrate_config.py --config <CONFIG> --owner-source user_md --write"},{"language":"bash","snippet":"python3 scripts/validate_character_profile.py --profile <CHARACTER_PROFILE>\npython3 scripts/validate_day_schedule.py --config <CONFIG> --path <DAY_SCHEDULE>"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: cyber-girlfriend\ndescription: Build or customize an owner-only proactive companion system with a cyber-girlfriend persona, Markdown private-life context, lightweight relationship memory, and OpenClaw presence cron delivery.\nmetadata:\n  version: \"2.2.0\"\n---\n\n# Cyber Girlfriend\n\nUse this skill when the user wants an owner-only proactive companion instead of a purely reactive assistant.\n\nThis skill gives the owner:\n- proactive companion messages sent on a real schedule\n- a core persona in `character-profile.md`\n- daily private-life context in `day-schedule.md`\n- configurable quiet hours and event-level pacing\n- lightweight continuity in `life-log.jsonl`\n- optional event media such as photos, audio, or video\n\n## Quick Start\n\nThis skill is meant to be set up by an agent, not by hand.\n\nIf the user wants the default setup, the simplest explicit invocation is:\n\n> Use $cyber-girlfriend to help me set up a cyber girlfriend.\n\nThe agent should then gather the minimum inputs, create or update the local files, wire the default cron jobs, and validate the install before claiming success.\n\n## What The User Needs\n\nFor a normal install, the user only needs:\n- an OpenClaw runtime\n- one working delivery route to the owner\n- a few persona and daily-life anchors\n\nThe user should not need to:\n- hand-write JSON\n- hand-write cron payloads\n- manually wire runner contracts\n- read every reference file before getting started\n\n## Default Setup Shape\n\nThe recommended starter setup is:\n- one daily schedule builder job that writes `day-schedule.md`\n- one `companion-presence` automation that runs the deterministic tick wrapper as an exact Gateway command payload\n- 1-4 optional life anchors that are written into `day-schedule.md`\n\nThose anchors are life facts, not guaranteed sends.\n\n## What The Agent Sets Up\n\nThe current default active path has two small steps:\n- `scripts/companion_presence_tick.py --config <CONFIG>`\n- inside that wrapper, `scripts/companion_run.py --stage prepare --no-record-pending`\n\nOn OpenClaw versions that support command automations, cron invokes the wrapper directly with an exact argv payload; no model turn is used merely to launch the script. The wrapper reads local state through the prepare runner and exits quietly when no current event should send. Only when prepare returns `status = \"ok\"` does it derive a fresh dispatch-scoped companion session from the prepared run id. That session writes the first-person story, but text delivery goes through `companion_presence_tick.py --send-story --story-stdin`, which reloads the saved delivery contract from the dispatch lock, sends with the external OpenClaw CLI, and commits state only after successful text delivery. If the matched event asks for media, media generation starts after `--send-story` succeeds and finishes asynchronously. The wrapper also starts a deterministic recent-media watcher for the same dispatch session, so generated media is delivered through the prepared delivery contract even if th"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn71yvy6nsxy27kp8krr9879ys80gv9h\",\n  \"slug\": \"cyber-girlfriend\",\n  \"version\": \"2.2.0\",\n  \"publishedAt\": 1788403624689\n}"},{"path":"references/agent-first-time-qa-template.md","content":"# Agent First-Time Q&A Template\n\nUse this only when an agent needs literal onboarding wording for a brand-new setup conversation.\n\nThis file is a conversation aid, not the source of truth.\nFor setup order and defaults, read [first-time-setup.md](./first-time-setup.md).\nFor field ownership, read [configuration.md](./configuration.md).\nFor presence cron wiring, read [presence-integration.md](./presence-integration.md).\n\n## Core Rule\n\nDo not ask the user to write prompts during first setup.\n\nThe agent should:\n- collect routing and pacing decisions\n- collect enough real-world persona anchors when private life is enabled\n- ask whether any fixed life anchors should always enter the day\n- translate the result into config, Markdown life files, and cron jobs\n\n## Recommended Opening\n\nUse wording like:\n\n> 我先按首配流程帮你收最小必要信息，不让你自己写 prompt。先确认投递目标，再写她的人设和生活锚点，最后我来落配置、日程和 presence cron。\n\n## Default Question Order\n\n### 1. Delivery route\n\n> 陪伴消息准备发到哪个渠道？把目标 id 一起给我。  \n> 如果这个渠道发消息还要指定发送账号，也一起给我。\n\nCapture:\n- `delivery.channel`\n- `delivery.owner_target`\n- `delivery.account` when needed\n\n### 2. Owner profile source\n\n> owner 信息你要我从 OpenClaw 的 USER.md 导入，还是你手动给我一份简短自定义？  \n> 我只需要区分 owner 和 companion，不会把渠道账号或 session id 塞进 prompt。\n\nCapture:\n- `owner_profile.source`: `user_md`, `manual`, or `none`\n- stable owner identity fields only\n\n### 3. Character profile reality anchors\n\n> 如果你要她有活人感，我还需要她现实里的身份信息：年龄或阶段、学生/上班/创作者是哪种、城市、平时更常看什么内容。\n\nCapture when available:\n- 基础身份\n- 兴趣与内容偏好\n- 关系表达和禁区边界\n\n### 4. Required life events\n\n> 有没有什么你希望她每天一定会经历、但不一定每次都发消息的事？比如通勤、晚课、健身、夜里收尾、固定拍照散步。  \n> 有的话我会写成 `必定发生：是` 的日程锚点。\n\nFor each anchor, ask only:\n- 时间或时间窗口\n- 持续多久\n- 她在哪里\n- 她正在做什么\n- 可以自然提到什么\n- 可以怎么轻轻接 owner\n- 不要写成什么\n\n### 5. Quiet hours\n\n> 安静时段你想设几点到几点？没要求我就用 01:00 到 08:00。\n\n### 6. Confirmation before writing\n\n> 执行前我先确认范围：会写 `config.local.json`、`character-profile.md`、`day-schedule.md` 和运行状态文件；创建或更新 `companion-build-day-schedule`、`companion-presence` 两个定时任务，并把任务名、时间和 payload 类型列给你。投递渠道、账号和目标只显示脱敏值。  \n> 日程生成和命中事件写作会访问公开网页；验收会向刚确认的 owner 目标发送一条可见测试消息。需要暂停时只禁用这两个任务并保留配置和状态；永久删除任务会另行征得你的明确同意。  \n> 请确认后我再执行这些写入、联网搜索、任务变更和一次真实发送。\n\n## Fast One-Shot Version\n\nWhen the user wants the shortest possible onboarding, ask this bundle:\n\n> 我按最快首配来收信息，你回我这些就行：  \n> 1. 发到哪个渠道，目标 id 是什么，需不需要指定发送账号  \n> 2. 她现实里是什么身份：年龄/阶段、学校或工作、城市、常看的内容方向  \n> 3. 有没有必定发生的日常锚点：时间、持续多久、场景、正在做什么  \n> 4. 安静时段，如果没要求我就用默认值  \n> 5. 我会先预览要写的文件、两个任务的名称/时间/payload、脱敏投递目标、公开搜索和一次测试发送；你明确确认后我才执行。暂停只禁用任务并保留配置/状态，永久删除需另行确认。\n\n## Ownership Reminder\n\nMap answers like this:\n\n| Answer type | Goes to config | Goes to Markdown / cron |\n| --- | --- | --- |\n| delivery route | yes | used by presence send step |\n| owner identity boundary | yes | used to separate owner from companion |\n| character profile reality anchors | `character_profile_path` | `character-profile.md` |\n| quiet hours | yes | validators enforce schedule windows |\n| required life anchors | yes | `day-schedule.md` as `必定发生：是` |\n| presence cadence | no | OpenClaw cron |\n\n## Do Not Do These\n\nDo not:\n-"},{"path":"references/configuration.md","content":"# Configuration\n\nKeep the runtime file name as `config.local.json` for compatibility.\nDo **not** rename it for v2. Upgrade by adding `\"version\": 2` and the new sections.\n\n## Design Goal\n\nAsk the user for as little as possible.\n\nSplit the config into three kinds of fields:\n1. **Companion character profile pointer** — where the Markdown character profile lives\n2. **Owner identity boundary** — who the owner is, kept separate from companion life\n3. **Agent-generated life model** — derived rhythm, current day schedule, continuity state\n\nThe user should mostly fill the first two kinds. The agent/runtime should generate the third kind.\n\n## Field Ownership Rule\n\nFor first-time setup, keep this split strict:\n\n- `config.local.json` stores profile paths, delivery, pacing policy, runtime paths, and optional long-lived source toggles\n- `character-profile.md` stores the companion's core identity, tone, relationship expression, interests, and lived anchors\n- `life_schedule.day_schedule.required_events` stores stable user-defined life anchors\n- presence automation payloads store only an exact wrapper argv, not prompt prose\n- generated state files store derived rhythm, continuity, and current day schedules\n\nDo not turn `config.local.json` into a dump of prompt prose.\n\n## Required Sections\n\n### `version`\n\n- Set to `2`\n\n### `character_profile_path`\n\nPath to the companion's core Markdown character profile.\n\nRecommended:\n- `./state/character-profile.md`\n\nThe published example is:\n- `assets/character-profile.example.md`\n\nThis file now owns the full companion persona:\n- name and owner-facing nickname\n- age / life stage\n- identity role\n- city / district\n- school, work, or creative background\n- personality, interests, entertainment tastes, expression style, relationship style, and safety boundaries\n\n### `persona` (deprecated)\n\nDeprecated compatibility cache. New installs should not ask users to maintain this JSON object.\n\nIf an older `config.local.json` still has `persona`, it may be used as a migration source or fallback.\nThe forward direction is to migrate those fields into `character-profile.md` and keep config JSON focused on machine-readable runtime settings.\n\n### `owner_profile`\n\nOptional lightweight owner identity boundary. Its job is not to over-control style; its job is to stop the companion's school, work, friends, dorm, class, or other private-life material from being projected onto the owner.\n\nRecommended fields:\n- `source` — `manual | user_md | none`\n- `user_md_path` — optional path when importing from OpenClaw `USER.md`\n- `name`\n- `preferred_name`\n- `pronouns`\n- `location`\n- `timezone`\n- `identity_summary`\n- `not_assumptions` — optional user-defined taboos or identity assumptions to avoid\n\nFor first-time setup or upgrade, ask one product question:\n`owner 信息要从 USER.md 导入，还是你手动自定义？`\n\nIf the user picks `USER.md`, import only stable identity fields such as name, preferred name, pronouns, location, and timezone. Do not copy messaging-platform session keys, accou"},{"path":"references/contract-schema.md","content":"# Turn Contract\n\n`scripts/companion_presence_tick.py` is the default presence automation entrypoint. New OpenClaw installations invoke it directly with an exact argv command payload; the wrapper runs `companion_run.py` for fresh prepare, exits quietly on skip, and starts a fresh dispatch-scoped companion session only when a current event is matched.\n\n## Prepare Stage\n\nCommand:\n\n```bash\npython3 <SKILL_DIR>/scripts/companion_run.py --stage prepare --config <CONFIG> --no-record-pending\n```\n\nAutomation command payload:\n\n```json\n{\n  \"kind\": \"command\",\n  \"argv\": [\n    \"python3\",\n    \"<SKILL_DIR>/scripts/companion_presence_tick.py\",\n    \"--config\",\n    \"<CONFIG>\"\n    ],\n    \"cwd\": \"<SKILL_DIR>\",\n    \"env\": {\n      \"PYTHONUNBUFFERED\": \"1\"\n    },\n    \"timeoutSeconds\": 120,\n  \"outputMaxBytes\": 65536\n}\n```\n\nPrepare selects the current `day-schedule.md` event by real local time. Events marked `必定发生：是` are life facts, not guaranteed sends.\n\nRequired output fields:\n- `status`\n- `run_id`\n- `life_context`\n- `delivery_contract`\n- `media_contract`\n- `state_commit`\n- `next_step`\n\n`life_context` is structured and must contain:\n- `generated_at`\n- `timezone`\n- `speaker`\n- `today`\n- `event`\n- `reality_check`\n\nOptional:\n- `delivery_mood`\n\nThe tick wrapper passes the prepared contract to a fresh dispatch-scoped companion session when status is ok. That session writes one first-person companion presence story directly from `life_context`. There is no separate render stage and no `render_spec` in the current architecture. A prepared run id is appended to the configured base session key, so an archived session from an earlier event is never resumed.\n\nPrepare output must not include duplicated task fields or local-only execution hints:\n- no top-level `primary_goal`\n- no `render_spec`\n- no local runbook fields\n- no private local paths or channel identifiers beyond the configured delivery contract\n\nAgents should not infer delivery ownership from prose. Follow these fields:\n- `delivery_contract.send_in_main_turn = true`: text may be sent in the main turn.\n- `media_contract.kind = event_media`: the current event requires media.\n- `media_contract.async = true`: OpenClaw media generation is asynchronous.\n- `media_contract.tool_name`: the runtime-selected async media generator for the matched media type.\n- `media_contract.completion_event_is_sender = true`: the media task is asynchronous and will produce a native OpenClaw completion event.\n- `media_contract.callback_context.strategy = same_stable_session`: \"stable\" means the same dispatch-scoped session for the lifetime of this one media task, not a session reused across events.\n- `media_contract.callback_context.requires_original_session_context = true`: the completion turn relies on that dispatch session context to keep the original delivery contract available.\n- `state_commit.when`: defines when pacing state can be committed.\n\nThe presence agent must send text through `companion_presence_tick.py --send-story --story-stdin`, not "}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2219,"uniquenessScore":39,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T09:30:52.667Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T21:23:30.554Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}