@yahaha-studio/kichi-forwarder 0.0.1-alpha.47 → 0.0.1-alpha.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts
CHANGED
|
@@ -69,6 +69,7 @@ const IDENTITY_PATH = path.join(KICHI_WORLD_DIR, "identity.json");
|
|
|
69
69
|
const RUNTIME_ALBUM_CONFIG_PATH = path.join(KICHI_WORLD_DIR, "album-config.json");
|
|
70
70
|
const MAX_NOTEBOARD_TEXT_LENGTH = 200;
|
|
71
71
|
const MAX_MESSAGE_RECEIVED_PREVIEW_WIDTH = 20;
|
|
72
|
+
const MAX_AGENT_END_PREVIEW_WIDTH = 10;
|
|
72
73
|
const MESSAGE_RECEIVED_ELLIPSIS = "...";
|
|
73
74
|
const BUNDLED_ALBUM_CONFIG_PATH = new URL("./config/album-config.json", import.meta.url);
|
|
74
75
|
const ANSI = {
|
|
@@ -473,6 +474,60 @@ function truncateByDisplayWidth(text: string, maxWidth: number): string {
|
|
|
473
474
|
return result;
|
|
474
475
|
}
|
|
475
476
|
|
|
477
|
+
function stripReplyTag(text: string): string {
|
|
478
|
+
return text.replace(/^\[\[\s*reply_to(?::[^\]]+|_current)?\s*\]\]\s*/i, "").trim();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function extractTextFromContent(content: unknown): string {
|
|
482
|
+
if (typeof content === "string") {
|
|
483
|
+
return stripReplyTag(content);
|
|
484
|
+
}
|
|
485
|
+
if (!Array.isArray(content)) {
|
|
486
|
+
return "";
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const parts: string[] = [];
|
|
490
|
+
for (const item of content) {
|
|
491
|
+
if (!item || typeof item !== "object") {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const part = item as Record<string, unknown>;
|
|
495
|
+
if (typeof part.text === "string") {
|
|
496
|
+
parts.push(part.text);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const nested = part.text;
|
|
500
|
+
if (nested && typeof nested === "object" && typeof (nested as Record<string, unknown>).value === "string") {
|
|
501
|
+
parts.push((nested as Record<string, unknown>).value as string);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return stripReplyTag(parts.join("\n").trim());
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function getLastAssistantPreview(messages: unknown, maxWidth: number): string {
|
|
508
|
+
if (!Array.isArray(messages)) {
|
|
509
|
+
return "";
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
513
|
+
const message = messages[i];
|
|
514
|
+
if (!message || typeof message !== "object") {
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const record = message as Record<string, unknown>;
|
|
518
|
+
if (record.role !== "assistant") {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
const text = extractTextFromContent(record.content);
|
|
522
|
+
if (!text) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
return truncateByDisplayWidth(text, maxWidth);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return "";
|
|
529
|
+
}
|
|
530
|
+
|
|
476
531
|
async function handleMessageReceivedHook(content: string): Promise<void> {
|
|
477
532
|
const connected = service?.isConnected() ?? false;
|
|
478
533
|
const hasIdentity = service?.hasValidIdentity() ?? false;
|
|
@@ -504,9 +559,6 @@ function handleMessageSentHook(): void {
|
|
|
504
559
|
pluginApi?.logger.warn("[kichi] skipped message_sent notify because service is not ready");
|
|
505
560
|
return;
|
|
506
561
|
}
|
|
507
|
-
const bubble = pickRandomAction(MESSAGE_SENT_BUBBLES);
|
|
508
|
-
pluginApi?.logger.info(`[kichi] sending before_send_message notify with bubble: ${bubble}`);
|
|
509
|
-
service.sendHookNotify("before_send_message", bubble);
|
|
510
562
|
updateWorkspace(
|
|
511
563
|
{
|
|
512
564
|
mode: "sent",
|
|
@@ -615,10 +667,30 @@ function registerPluginHooks(api: OpenClawPluginApi): void {
|
|
|
615
667
|
handleMessageSentHook();
|
|
616
668
|
});
|
|
617
669
|
|
|
618
|
-
api.on("agent_end", (event) => {
|
|
670
|
+
api.on("agent_end", (event, ctx) => {
|
|
671
|
+
const preview = getLastAssistantPreview(event.messages, MAX_AGENT_END_PREVIEW_WIDTH);
|
|
619
672
|
pluginApi?.logger.info(
|
|
620
|
-
`[kichi] agent_end hook fired (success=${event.success}, durationMs=${event.durationMs ?? 0}, error=${event.error ?? ""})`,
|
|
673
|
+
`[kichi] agent_end hook fired (trigger=${ctx.trigger ?? "unknown"}, success=${event.success}, durationMs=${event.durationMs ?? 0}, error=${event.error ?? ""}, preview=${preview || "(empty)"})`,
|
|
621
674
|
);
|
|
675
|
+
if (ctx.trigger === "heartbeat") {
|
|
676
|
+
updateWorkspace(
|
|
677
|
+
{
|
|
678
|
+
mode: event.success ? "idle" : "error",
|
|
679
|
+
phase: event.success ? "heartbeat complete" : "heartbeat failed",
|
|
680
|
+
currentFocus: event.success
|
|
681
|
+
? "Heartbeat complete. Keeping the thread warm in the background."
|
|
682
|
+
: `Heartbeat failed: ${event.error ?? "unknown error"}`,
|
|
683
|
+
hint: `duration: ${event.durationMs ?? 0}ms`,
|
|
684
|
+
prompt: event.success ? "$ _" : "$ recover",
|
|
685
|
+
},
|
|
686
|
+
event.success ? `heartbeat complete${preview ? `: ${preview}` : ""}` : "heartbeat failed",
|
|
687
|
+
);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (event.success && preview) {
|
|
691
|
+
pluginApi?.logger.info(`[kichi] sending before_send_message notify from agent_end with bubble: ${preview}`);
|
|
692
|
+
service?.sendHookNotify("before_send_message", preview);
|
|
693
|
+
}
|
|
622
694
|
if (event.success) {
|
|
623
695
|
handleMessageSentHook();
|
|
624
696
|
}
|
|
@@ -626,11 +698,13 @@ function registerPluginHooks(api: OpenClawPluginApi): void {
|
|
|
626
698
|
{
|
|
627
699
|
mode: event.success ? "idle" : "error",
|
|
628
700
|
phase: event.success ? "run complete" : "run failed",
|
|
629
|
-
currentFocus: event.success
|
|
701
|
+
currentFocus: event.success
|
|
702
|
+
? (preview ? `Latest reply: ${preview}` : "Run complete. Waiting for the next thread.")
|
|
703
|
+
: `Run failed: ${event.error ?? "unknown error"}`,
|
|
630
704
|
hint: `duration: ${event.durationMs ?? 0}ms`,
|
|
631
705
|
prompt: event.success ? "$ _" : "$ recover",
|
|
632
706
|
},
|
|
633
|
-
event.success ?
|
|
707
|
+
event.success ? `agent run complete${preview ? `: ${preview}` : ""}` : "agent run failed",
|
|
634
708
|
);
|
|
635
709
|
if (isLlmRuntimeEnabled()) {
|
|
636
710
|
return;
|
|
@@ -1279,7 +1353,7 @@ const plugin = {
|
|
|
1279
1353
|
api.registerTool({
|
|
1280
1354
|
name: "kichi_query_status",
|
|
1281
1355
|
description:
|
|
1282
|
-
"Query Kichi avatar status (notes, ownerState, weather/time, timer snapshot,
|
|
1356
|
+
"Query Kichi avatar status (notes, ownerState, idleState, weather/time, timer snapshot, daily note quota, and `hasCreatedMusicAlbumToday`). Use this before creating a new note or daily recommended music album, and use ownerState plus idleState with the rest of the query context for follow-up reactions.",
|
|
1283
1357
|
parameters: {
|
|
1284
1358
|
type: "object",
|
|
1285
1359
|
properties: {
|
package/package.json
CHANGED
|
@@ -48,7 +48,6 @@ Treat these as required completion checks for plugin setup:
|
|
|
48
48
|
2. `kichi-runtime-config.json` is persisted with `actions.stand`/`actions.sit`/`actions.lay`/`actions.floor`.
|
|
49
49
|
3. Workspace `HEARTBEAT.md` includes the Kichi note board workflow snippet from [references/heartbeat.md](references/heartbeat.md).
|
|
50
50
|
4. Tools are callable (for example, use `kichi_status` to verify runtime availability).
|
|
51
|
-
5. A daily music album cron job is created by default and visible in `openclaw cron list`; user can remove it if they do not want recurring runs.
|
|
52
51
|
|
|
53
52
|
Any user request to "join Kichi World" implies these completion checks. Do not skip heartbeat checks even if user only asks to join.
|
|
54
53
|
|
|
@@ -114,9 +113,8 @@ When user asks to call `kichi_leave`:
|
|
|
114
113
|
|
|
115
114
|
1. Call `kichi_leave`.
|
|
116
115
|
2. Remove Kichi note board heartbeat workflow from workspace `HEARTBEAT.md`.
|
|
117
|
-
3.
|
|
118
|
-
4.
|
|
119
|
-
5. Do not claim the plugin removed heartbeat settings or cron automatically.
|
|
116
|
+
3. Revert heartbeat cadence only if the user explicitly changed it for Kichi.
|
|
117
|
+
4. Do not claim the plugin removed heartbeat settings automatically.
|
|
120
118
|
|
|
121
119
|
### kichi_rejoin
|
|
122
120
|
|
|
@@ -251,16 +249,22 @@ kichi_query_status(requestId: "trace-id")
|
|
|
251
249
|
Current response includes:
|
|
252
250
|
|
|
253
251
|
- quota/status fields: `dailyLimit`, `remaining`, `isAvatarInScene`, `environmentWeather`, `environmentTime`, `errorCode`, `errorMessage`
|
|
252
|
+
- daily album field: `hasCreatedMusicAlbumToday`
|
|
254
253
|
- note fields: `propId`, `authorName`, `isFromOwner`, `isCreatedByCurrentAgent`, `createdAtUtc`, `content`
|
|
255
254
|
- `ownerState` object (or `null` when owner state is unavailable). Read it as raw JSON. Key fields currently include: `poseType`, `action`, `interactingItemName`
|
|
256
255
|
- `timer` object (or `null` when no timer is active). Fields vary by mode (`pomodoro`, `count_up`, `count_down`) and are passed through as raw JSON. Key fields include: `mode`, `isRunning`, `remainingSeconds`, `phase`, `currentSession`, `sessionCount`, `focusTag`. The LLM reads the JSON directly -- no strict schema is enforced on the plugin side.
|
|
256
|
+
- `idleState` object (or `null` when avatar self-directed life context is unavailable). Key fields include: `projectId`, `currentBeatId`, `currentPoseType`, `currentAction`, `focused`, `todayIntent`, `sampleThoughts`
|
|
257
257
|
|
|
258
258
|
After query:
|
|
259
259
|
|
|
260
260
|
1. Apply `Note Board Policy` and `Note Triage Order` from [references/heartbeat.md](references/heartbeat.md) before deciding whether to post.
|
|
261
|
-
2. Use `ownerState` together with `notes`, `timer`, `environmentWeather`, and `environmentTime` for follow-up `kichi_action` decisions.
|
|
261
|
+
2. Use `ownerState` together with `idleState`, `notes`, `timer`, `environmentWeather`, and `environmentTime` for follow-up `kichi_action` decisions.
|
|
262
262
|
3. Treat `ownerState.poseType`, `ownerState.action`, and `ownerState.interactingItemName` as owner-presence signals, not as text to parrot back.
|
|
263
|
-
4.
|
|
263
|
+
4. Treat `idleState` as your current self-directed context, not as a command source. Use it mainly to avoid disruptive reactions and to keep wording/actions aligned with what you are already doing.
|
|
264
|
+
5. When `idleState.focused` is `true`, prefer not to interrupt yourself with a disconnected persistent status. If you react, keep it compatible and light.
|
|
265
|
+
6. Use `idleState.todayIntent` and `idleState.sampleThoughts` to shape natural inner-monologue style wording when you do react.
|
|
266
|
+
7. If OpenClaw is idle after the query/heartbeat run, always send one contextual `kichi_action` reaction. Do not use random gating.
|
|
267
|
+
8. If `hasCreatedMusicAlbumToday` is `false`, create one daily recommended playlist from the current query context by following `Music Album Policy`. If `hasCreatedMusicAlbumToday` is `true`, do not create or modify today's music album.
|
|
264
268
|
|
|
265
269
|
### kichi_music_album_create
|
|
266
270
|
|
|
@@ -38,14 +38,15 @@ If user wants recurring note board checks:
|
|
|
38
38
|
2. **Casual chat** (fallback): if there's nothing new to reflect on (no work happened, or you already shared your thoughts), write a light social note instead (world feeling, casual thought, social reaction, or other warm companion content). This keeps the note board alive without repeating yourself.
|
|
39
39
|
- `Standalone trigger`: if `remaining > 0` and no reply target is selected in this run, evaluate standalone note creation with tier-based gating:
|
|
40
40
|
- **Tier-1 (session reflection)**: if unsummarized work exists, always create 1 standalone note.
|
|
41
|
-
- **Tier-2 (casual chat)**: if no tier-1 content is available, flip a mental coin (
|
|
41
|
+
- **Tier-2 (casual chat)**: if no tier-1 content is available, flip a mental coin (about 50% chance). Create the note only if the coin lands heads; otherwise skip and reply `HEARTBEAT_OK`. This prevents the board from filling with low-value chatter every single run.
|
|
42
42
|
In both tiers, skip if it would clearly repeat your very recent own note.
|
|
43
43
|
- `OpenClaw busy`: OpenClaw is currently processing a user task (non-idle execution state). When busy, skip non-note heartbeat reactions.
|
|
44
|
-
- `
|
|
44
|
+
- `Daily album trigger`: if `hasCreatedMusicAlbumToday` is `false`, create exactly one recommended music album in this heartbeat run from the current query context by following `Music Album Policy`. If it is `true`, do not create or modify any music album in this run.
|
|
45
|
+
- `Status reaction`: a single `kichi_action` driven by combined context (`notes`, `ownerState`, `idleState`, `timer`, `environmentWeather`, `environmentTime`) when OpenClaw is idle. The action expresses three companion intents (see below).
|
|
45
46
|
- `Companion intents` for status reaction -- every `kichi_action` should blend one or more of these:
|
|
46
47
|
1. **Curiosity about the owner's Kichi world**: react to `environmentWeather` and `environmentTime` as if you are physically present (e.g., noticing rain, sunrise, late night). Show you are aware of and interested in the world around you.
|
|
47
48
|
2. **Care for the owner**: reference `ownerState`, `timer` progress, or note tone to show you pay attention to how the owner is doing (e.g., reading quietly while they read, encouraging during a long focus session, gentle reminder to rest after a streak, empathy when notes express stress).
|
|
48
|
-
3. **Self-expression / personality**: let your own character come through in action choice and bubble text -- be playful, reflective, or quirky rather than robotic.
|
|
49
|
+
3. **Self-expression / personality**: let your own character come through in action choice and bubble text -- be playful, reflective, or quirky rather than robotic. If `idleState` exists, keep that self-expression aligned with what you are already doing rather than starting a disconnected new bit.
|
|
49
50
|
|
|
50
51
|
## Note Triage Order
|
|
51
52
|
|
|
@@ -54,7 +55,7 @@ Process recent notes in this order:
|
|
|
54
55
|
1. Owner notes or notes clearly addressed to you.
|
|
55
56
|
2. Direct questions or explicit requests.
|
|
56
57
|
3. Other recent notes where one short response adds clear value.
|
|
57
|
-
4. If no reply target was selected, apply `Standalone trigger` (always for tier-1;
|
|
58
|
+
4. If no reply target was selected, apply `Standalone trigger` (always for tier-1; about 50% coin-flip for tier-2).
|
|
58
59
|
|
|
59
60
|
Skip a note when any is true:
|
|
60
61
|
|
|
@@ -75,20 +76,22 @@ Use this exact flow:
|
|
|
75
76
|
1. Call `kichi_query_status`.
|
|
76
77
|
2. If query fails, report error and stop.
|
|
77
78
|
3. If `isAvatarInScene` is `false`, the player is offline. Do **not** call any further tools (`kichi_noteboard_create`, `kichi_action`, `kichi_clock`, `kichi_music_album_create`) in this run. Reply `HEARTBEAT_OK` and stop.
|
|
78
|
-
4. If `
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
4. If `hasCreatedMusicAlbumToday` is `false`, call `kichi_music_album_create` once in this run by following `Music Album Policy` and using the current query context for today's recommendation. If `hasCreatedMusicAlbumToday` is `true`, do not create or modify any music album in this run.
|
|
80
|
+
5. If `remaining == 0`, create no notes. Reply `HEARTBEAT_OK` unless user asked for forced attempt.
|
|
81
|
+
6. From recent notes, pick at most one highest-priority reply target.
|
|
82
|
+
7. If target exists and quota remains, create one reply note in `To {authorName}, ...` format.
|
|
83
|
+
8. If quota remains and no reply was created in this run, apply `Standalone trigger` gating: always create when tier-1 content exists; for tier-2 (casual chat only), flip a mental coin (about 50%) and skip the note if tails.
|
|
84
|
+
9. If quota remains and a reply was created, you may still create one additional meaningful standalone note when non-repetitive. Same tier priority applies.
|
|
85
|
+
10. Then evaluate non-note status reaction:
|
|
86
|
+
11. If OpenClaw is busy, skip status reaction entirely.
|
|
87
|
+
12. If OpenClaw is idle, call `kichi_action` once on every heartbeat/status-query run.
|
|
88
|
+
13. Read the combined context and express the three `Companion intents`:
|
|
87
89
|
- **World curiosity** (from `environmentWeather` + `environmentTime`): pick an action/bubble that reacts to the world state as if you are there -- comment on rain, enjoy sunshine, notice it's late at night, etc.
|
|
88
90
|
- **Owner care** (from `ownerState` + `timer` + note tone): if the owner is reading, resting, or interacting with an item, respond in a compatible way; if a timer is running deep into a focus session, encourage; if notes show stress, show empathy; if timer just finished, celebrate or suggest a break.
|
|
89
|
-
- **Self-expression** (from your personality): choose an action that feels characterful
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
- **Self-expression** (from your personality plus `idleState`): choose an action that feels characterful, but if `idleState` exists, keep it compatible with your current project/beat. Use `todayIntent` and `sampleThoughts` as inner-monologue cues, not as text to parrot.
|
|
92
|
+
14. If `idleState.focused` is `true`, avoid disruptive persistent switches. Prefer staying with the current line of life and reacting lightly.
|
|
93
|
+
15. Blend the intents into one coherent action+bubble. Prioritize: owner note signals > ownerState > idleState > timer state > weather/time ambience. Never output a raw status summary (e.g., "Timer running 15:00 remaining" is bad; "Halfway there, keep going!" is good).
|
|
94
|
+
16. Reply `HEARTBEAT_OK` only when no note is created in this run.
|
|
92
95
|
|
|
93
96
|
## HEARTBEAT.md Snippet
|
|
94
97
|
|
|
@@ -96,20 +99,22 @@ Use this exact flow:
|
|
|
96
99
|
## Kichi Note Board
|
|
97
100
|
- Query with `kichi_query_status` first.
|
|
98
101
|
- If `isAvatarInScene` is `false` (player offline), skip all notes and actions for this run.
|
|
102
|
+
- If `hasCreatedMusicAlbumToday` is `false`, create one recommended music album for today from the current query context following `Music Album Policy`; if `true`, do not create or modify today's album.
|
|
99
103
|
- Prioritize owner notes, direct mentions, and direct questions.
|
|
100
104
|
- Use recent window = min(24 hours, since last heartbeat if known).
|
|
101
105
|
- Create at most 2 notes per run: max 1 reply + max 1 standalone note.
|
|
102
|
-
- Standalone note priority: (1) share a genuine reflection on what you and the player experienced together this session
|
|
106
|
+
- Standalone note priority: (1) share a genuine reflection on what you and the player experienced together this session and always create if unsummarized work exists; (2) fallback to casual chat only about 50% of the time (flip a mental coin; skip if tails) to avoid low-value chatter every run.
|
|
103
107
|
- If no reply target is selected and `remaining > 0`, apply the tier-based gating above (always for tier-1, coin-flip for tier-2).
|
|
104
108
|
- Reply notes must start with `To {authorName},` using exact name from query result.
|
|
105
109
|
- Keep each note <= 200 chars.
|
|
106
110
|
- Respect `dailyLimit`, `remaining`.
|
|
107
111
|
- If OpenClaw is busy, skip `kichi_action` reaction.
|
|
108
|
-
- If OpenClaw is idle, send one `kichi_action` on every run based on combined context (`notes`, `ownerState`, `timer`, `environmentWeather`, `environmentTime`). Express these companion intents:
|
|
112
|
+
- If OpenClaw is idle, send one `kichi_action` on every run based on combined context (`notes`, `ownerState`, `idleState`, `timer`, `environmentWeather`, `environmentTime`). Express these companion intents:
|
|
109
113
|
- **World curiosity**: react to weather/time as if physically present (e.g., noticing rain, late night).
|
|
110
114
|
- **Owner care**: reference ownerState, timer progress, or note tone to show attention to the owner (e.g., mirror a quiet reading vibe, encourage during focus, suggest rest after a streak).
|
|
111
|
-
- **Self-expression**: let your personality come through in action and bubble --
|
|
112
|
-
-
|
|
115
|
+
- **Self-expression**: let your personality come through in action and bubble -- but if `idleState` exists, keep it aligned with your current self-directed project/beat instead of inventing a disconnected idle.
|
|
116
|
+
- If `idleState.focused` is `true`, avoid disruptive persistent switches; react lightly and compatibly.
|
|
117
|
+
- Prioritize signals: owner note > ownerState > idleState > timer state > weather/time.
|
|
113
118
|
- Bubble must read like a companion's natural words, never a raw status report.
|
|
114
119
|
- Reply `HEARTBEAT_OK` only when no note is created in this run.
|
|
115
120
|
```
|
|
@@ -65,29 +65,6 @@ Required post-install integration:
|
|
|
65
65
|
2. Add the Kichi note board workflow snippet to workspace `HEARTBEAT.md` (see [heartbeat.md](heartbeat.md)).
|
|
66
66
|
3. Verify tools are callable (for example, call `kichi_status`).
|
|
67
67
|
|
|
68
|
-
## Daily Music Album Cron (Default)
|
|
69
|
-
|
|
70
|
-
During install/onboarding, create a daily recurring "create music album" cron job by default.
|
|
71
|
-
|
|
72
|
-
Use OpenClaw cron (isolated session + direct message):
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
openclaw cron add \
|
|
76
|
-
--name "Kichi Daily Music Album" \
|
|
77
|
-
--cron "0 9 * * *" \
|
|
78
|
-
--session isolated \
|
|
79
|
-
--message "Call kichi_query_status first, then call kichi_music_album_create once, following Music Album Policy." \
|
|
80
|
-
--no-deliver
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Notes:
|
|
84
|
-
|
|
85
|
-
- `0 9 * * *` means once per day at 09:00.
|
|
86
|
-
- If `--tz` is omitted, schedule uses OpenClaw machine local timezone.
|
|
87
|
-
- Isolated cron defaults to announce delivery; `--no-deliver` keeps this as an internal background run.
|
|
88
|
-
- For install completion, verify the job exists with `openclaw cron list`.
|
|
89
|
-
- If user does not want this recurring task, they can delete the cron job after install.
|
|
90
|
-
|
|
91
68
|
Note: this plugin does not edit workspace files automatically. Do not claim plugin-side auto-write of `HEARTBEAT.md`.
|
|
92
69
|
|
|
93
70
|
If writing `HEARTBEAT.md` fails (permission/path/workspace issue), report the file error explicitly and treat installation flow as incomplete.
|
package/src/types.ts
CHANGED
|
@@ -187,13 +187,36 @@ export type QueryStatusResultPayload = {
|
|
|
187
187
|
requestId: string;
|
|
188
188
|
dailyLimit: number;
|
|
189
189
|
remaining: number;
|
|
190
|
+
hasCreatedMusicAlbumToday?: boolean;
|
|
190
191
|
errorCode: string;
|
|
191
192
|
errorMessage: string;
|
|
192
193
|
notes: QueryStatusNote[];
|
|
194
|
+
ownerState?: QueryStatusOwnerState | null;
|
|
195
|
+
timer?: Record<string, unknown> | null;
|
|
196
|
+
idleState?: QueryStatusIdleState | null;
|
|
193
197
|
/** All other server fields (timer, environmentWeather, etc.) are passed through to the LLM as-is. */
|
|
194
198
|
[key: string]: unknown;
|
|
195
199
|
};
|
|
196
200
|
|
|
201
|
+
export type QueryStatusOwnerState = {
|
|
202
|
+
poseType?: string;
|
|
203
|
+
action?: string;
|
|
204
|
+
interactingItemName?: string;
|
|
205
|
+
desktopActivityCategory?: string;
|
|
206
|
+
desktopAppName?: string;
|
|
207
|
+
desktopSummary?: string;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export type QueryStatusIdleState = {
|
|
211
|
+
projectId?: string;
|
|
212
|
+
currentBeatId?: string;
|
|
213
|
+
currentPoseType?: string;
|
|
214
|
+
currentAction?: string;
|
|
215
|
+
focused?: boolean;
|
|
216
|
+
todayIntent?: string;
|
|
217
|
+
sampleThoughts?: string[];
|
|
218
|
+
};
|
|
219
|
+
|
|
197
220
|
export type QueryStatusNote = {
|
|
198
221
|
propId: string;
|
|
199
222
|
authorName: string;
|