forge-openclaw-plugin 0.2.105 → 0.2.106
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.
|
@@ -6,11 +6,12 @@ import { updateWorkoutMetadata } from "./health.js";
|
|
|
6
6
|
import { canonicalizeMovementCategoryTags, listMovementPlaces, normalizeMovementCategoryTag, updateMovementPlace } from "./movement.js";
|
|
7
7
|
import { createHabitCheckIn, listHabits } from "./repositories/habits.js";
|
|
8
8
|
import { listGoals } from "./repositories/goals.js";
|
|
9
|
+
import { createNote } from "./repositories/notes.js";
|
|
9
10
|
import { listProjectSummaries } from "./services/projects.js";
|
|
10
11
|
import { listTasks, updateTask } from "./repositories/tasks.js";
|
|
11
12
|
import { claimTaskRun, completeTaskRun, focusTaskRun, heartbeatTaskRun, listTaskRuns, releaseTaskRun } from "./repositories/task-runs.js";
|
|
13
|
+
import { createTriggerReport, listBehaviorPatterns, listBehaviors, listEmotionDefinitions, listEventTypes, listModeProfiles, listPsycheValues, listTriggerReports } from "./repositories/psyche.js";
|
|
12
14
|
import { formatLocalDateKey } from "../../src/lib/date-keys.js";
|
|
13
|
-
const watchCapability = "watch-ready";
|
|
14
15
|
const watchHistoryStateSchema = z.enum(["aligned", "unaligned", "unknown"]);
|
|
15
16
|
const watchPromptKindSchema = z.enum([
|
|
16
17
|
"new_place",
|
|
@@ -414,7 +415,164 @@ function buildPendingPrompts(userId) {
|
|
|
414
415
|
});
|
|
415
416
|
return prompts.slice(0, 8);
|
|
416
417
|
}
|
|
417
|
-
function
|
|
418
|
+
function stringPayloadValue(payload, key) {
|
|
419
|
+
const value = payload[key];
|
|
420
|
+
return typeof value === "string" ? value.trim() : "";
|
|
421
|
+
}
|
|
422
|
+
function createWatchObservationNote(pairing, event, options) {
|
|
423
|
+
return createNote({
|
|
424
|
+
kind: "evidence",
|
|
425
|
+
title: options.title,
|
|
426
|
+
slug: "",
|
|
427
|
+
spaceId: "",
|
|
428
|
+
parentSlug: null,
|
|
429
|
+
indexOrder: 0,
|
|
430
|
+
showInIndex: false,
|
|
431
|
+
aliases: [],
|
|
432
|
+
summary: "",
|
|
433
|
+
contentMarkdown: options.contentLines
|
|
434
|
+
.filter((line) => line.trim().length > 0)
|
|
435
|
+
.join("\n") || options.title,
|
|
436
|
+
author: "Apple Watch",
|
|
437
|
+
tags: ["watch", "psyche", "Self-observation", ...(options.tags ?? [])],
|
|
438
|
+
links: options.links ?? [],
|
|
439
|
+
destroyAt: null,
|
|
440
|
+
sourcePath: "",
|
|
441
|
+
frontmatter: {
|
|
442
|
+
observedAt: event.recordedAt,
|
|
443
|
+
watchEventType: event.eventType,
|
|
444
|
+
watchPromptId: event.promptId ?? null
|
|
445
|
+
},
|
|
446
|
+
revisionHash: "",
|
|
447
|
+
lastSyncedAt: null,
|
|
448
|
+
userId: pairing.user_id
|
|
449
|
+
}, { source: "system", actor: "Apple Watch" });
|
|
450
|
+
}
|
|
451
|
+
function projectPsycheEvent(pairing, event) {
|
|
452
|
+
if (event.eventType === "trigger_capture") {
|
|
453
|
+
const trigger = stringPayloadValue(event.payload, "trigger");
|
|
454
|
+
const eventTypeLabel = stringPayloadValue(event.payload, "eventTypeLabel");
|
|
455
|
+
const eventTypeId = stringPayloadValue(event.payload, "eventTypeId") || null;
|
|
456
|
+
const emotion = stringPayloadValue(event.payload, "emotion");
|
|
457
|
+
const outcome = stringPayloadValue(event.payload, "outcome");
|
|
458
|
+
const intensity = stringPayloadValue(event.payload, "intensity");
|
|
459
|
+
const situation = stringPayloadValue(event.payload, "situation") ||
|
|
460
|
+
stringPayloadValue(event.payload, "choice") ||
|
|
461
|
+
trigger ||
|
|
462
|
+
eventTypeLabel ||
|
|
463
|
+
"Watch trigger observation";
|
|
464
|
+
const report = createTriggerReport({
|
|
465
|
+
title: trigger ? `Watch trigger: ${trigger}` : "Watch trigger observation",
|
|
466
|
+
status: "draft",
|
|
467
|
+
eventTypeId,
|
|
468
|
+
customEventType: eventTypeId ? "" : eventTypeLabel || trigger || "Watch trigger",
|
|
469
|
+
eventSituation: situation,
|
|
470
|
+
occurredAt: event.recordedAt,
|
|
471
|
+
emotions: emotion
|
|
472
|
+
? [
|
|
473
|
+
{
|
|
474
|
+
emotionDefinitionId: stringPayloadValue(event.payload, "emotionId") || null,
|
|
475
|
+
label: emotion,
|
|
476
|
+
intensity: intensity ? Number(intensity) || 0 : 0,
|
|
477
|
+
note: "Captured from Apple Watch."
|
|
478
|
+
}
|
|
479
|
+
]
|
|
480
|
+
: [],
|
|
481
|
+
thoughts: [],
|
|
482
|
+
behaviors: outcome
|
|
483
|
+
? [
|
|
484
|
+
{
|
|
485
|
+
text: outcome,
|
|
486
|
+
mode: "",
|
|
487
|
+
behaviorId: stringPayloadValue(event.payload, "behaviorId") || null
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
: [],
|
|
491
|
+
consequences: {
|
|
492
|
+
selfShortTerm: [],
|
|
493
|
+
selfLongTerm: [],
|
|
494
|
+
othersShortTerm: [],
|
|
495
|
+
othersLongTerm: []
|
|
496
|
+
},
|
|
497
|
+
linkedPatternIds: stringPayloadValue(event.payload, "patternId")
|
|
498
|
+
? [stringPayloadValue(event.payload, "patternId")]
|
|
499
|
+
: [],
|
|
500
|
+
linkedValueIds: stringPayloadValue(event.payload, "valueId")
|
|
501
|
+
? [stringPayloadValue(event.payload, "valueId")]
|
|
502
|
+
: [],
|
|
503
|
+
linkedGoalIds: [],
|
|
504
|
+
linkedProjectIds: [],
|
|
505
|
+
linkedTaskIds: [],
|
|
506
|
+
linkedBehaviorIds: stringPayloadValue(event.payload, "behaviorId")
|
|
507
|
+
? [stringPayloadValue(event.payload, "behaviorId")]
|
|
508
|
+
: [],
|
|
509
|
+
linkedBeliefIds: [],
|
|
510
|
+
linkedModeIds: stringPayloadValue(event.payload, "modeId")
|
|
511
|
+
? [stringPayloadValue(event.payload, "modeId")]
|
|
512
|
+
: [],
|
|
513
|
+
modeOverlays: [],
|
|
514
|
+
schemaLinks: [],
|
|
515
|
+
modeTimeline: [],
|
|
516
|
+
nextMoves: [],
|
|
517
|
+
userId: pairing.user_id
|
|
518
|
+
}, { source: "system", actor: "Apple Watch" });
|
|
519
|
+
const note = createWatchObservationNote(pairing, event, {
|
|
520
|
+
title: "Watch trigger observation",
|
|
521
|
+
contentLines: [
|
|
522
|
+
`Trigger: ${trigger || eventTypeLabel || "unspecified"}`,
|
|
523
|
+
emotion ? `Emotion: ${emotion}` : "",
|
|
524
|
+
outcome ? `Outcome: ${outcome}` : "",
|
|
525
|
+
situation ? `Situation: ${situation}` : ""
|
|
526
|
+
],
|
|
527
|
+
links: [{ entityType: "trigger_report", entityId: report.id, anchorKey: null }],
|
|
528
|
+
tags: ["trigger"]
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
status: "projected",
|
|
532
|
+
details: {
|
|
533
|
+
target: "psyche_trigger_report",
|
|
534
|
+
reportId: report.id,
|
|
535
|
+
noteId: note.id
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (event.eventType === "emotion_check_in" ||
|
|
540
|
+
event.eventType === "routine_check" ||
|
|
541
|
+
event.eventType === "mark_moment") {
|
|
542
|
+
const emotion = stringPayloadValue(event.payload, "emotion");
|
|
543
|
+
const routine = stringPayloadValue(event.payload, "routine");
|
|
544
|
+
const surface = stringPayloadValue(event.payload, "surface");
|
|
545
|
+
const note = createWatchObservationNote(pairing, event, {
|
|
546
|
+
title: event.eventType === "emotion_check_in"
|
|
547
|
+
? "Watch emotion check-in"
|
|
548
|
+
: event.eventType === "routine_check"
|
|
549
|
+
? "Watch routine check"
|
|
550
|
+
: "Watch moment",
|
|
551
|
+
contentLines: [
|
|
552
|
+
emotion ? `Emotion: ${emotion}` : "",
|
|
553
|
+
routine ? `Routine: ${routine}` : "",
|
|
554
|
+
surface ? `Surface: ${surface}` : "",
|
|
555
|
+
stringPayloadValue(event.payload, "choice")
|
|
556
|
+
? `Choice: ${stringPayloadValue(event.payload, "choice")}`
|
|
557
|
+
: ""
|
|
558
|
+
],
|
|
559
|
+
tags: [event.eventType.replaceAll("_", "-")]
|
|
560
|
+
});
|
|
561
|
+
return {
|
|
562
|
+
status: "projected",
|
|
563
|
+
details: {
|
|
564
|
+
target: "psyche_observation_note",
|
|
565
|
+
noteId: note.id
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
function projectionForStoredEvent(pairing, event) {
|
|
572
|
+
const psycheProjection = projectPsycheEvent(pairing, event);
|
|
573
|
+
if (psycheProjection) {
|
|
574
|
+
return psycheProjection;
|
|
575
|
+
}
|
|
418
576
|
if (event.eventType === "place_label" && event.linkedContext.placeId) {
|
|
419
577
|
const nextLabel = typeof event.payload.label === "string" ? event.payload.label.trim() : "";
|
|
420
578
|
const categoryCandidate = Array.isArray(event.payload.categoryTags)
|
|
@@ -512,10 +670,11 @@ function projectionForStoredEvent(event) {
|
|
|
512
670
|
};
|
|
513
671
|
}
|
|
514
672
|
export function assertWatchReady(pairing) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
673
|
+
// Any valid mobile pairing can receive a compact watch snapshot. Older
|
|
674
|
+
// pairings were created before the explicit `watch-ready` capability existed;
|
|
675
|
+
// rejecting them leaves the installed watch app permanently empty even though
|
|
676
|
+
// the same pairing token can already sync HealthKit and movement data.
|
|
677
|
+
void pairing;
|
|
519
678
|
}
|
|
520
679
|
function compactTask(task) {
|
|
521
680
|
return {
|
|
@@ -735,6 +894,151 @@ function buildSyncSnapshot(pairing) {
|
|
|
735
894
|
actionReceiptCount: actionReceiptCount.count
|
|
736
895
|
};
|
|
737
896
|
}
|
|
897
|
+
function compactOption(option, payload = {}) {
|
|
898
|
+
const label = (option.label ?? option.title ?? "").trim();
|
|
899
|
+
return {
|
|
900
|
+
id: option.id ?? label,
|
|
901
|
+
label,
|
|
902
|
+
subtitle: (option.category ?? option.description ?? "").trim(),
|
|
903
|
+
payload
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
function compactFallbackOptions(labels, payloadKey) {
|
|
907
|
+
return labels.map((label) => ({
|
|
908
|
+
id: label,
|
|
909
|
+
label,
|
|
910
|
+
subtitle: "",
|
|
911
|
+
payload: { [payloadKey]: label }
|
|
912
|
+
}));
|
|
913
|
+
}
|
|
914
|
+
function buildWatchPsycheSnapshot(pairing) {
|
|
915
|
+
const userIds = pairing.user_id === "user_operator" ? undefined : [pairing.user_id];
|
|
916
|
+
const owned = (items) => userIds ? items.filter((item) => item.userId == null || userIds.includes(item.userId)) : items;
|
|
917
|
+
const events = owned(listEventTypes())
|
|
918
|
+
.slice(0, 8)
|
|
919
|
+
.map((eventType) => compactOption(eventType, {
|
|
920
|
+
eventTypeId: eventType.id,
|
|
921
|
+
eventTypeLabel: eventType.label
|
|
922
|
+
}))
|
|
923
|
+
.filter((option) => option.label.length > 0);
|
|
924
|
+
const emotions = owned(listEmotionDefinitions())
|
|
925
|
+
.slice(0, 10)
|
|
926
|
+
.map((emotion) => compactOption(emotion, {
|
|
927
|
+
emotionId: emotion.id,
|
|
928
|
+
emotion: emotion.label
|
|
929
|
+
}))
|
|
930
|
+
.filter((option) => option.label.length > 0);
|
|
931
|
+
const values = owned(listPsycheValues())
|
|
932
|
+
.slice(0, 8)
|
|
933
|
+
.map((value) => compactOption(value, {
|
|
934
|
+
valueId: value.id,
|
|
935
|
+
value: value.title
|
|
936
|
+
}))
|
|
937
|
+
.filter((option) => option.label.length > 0);
|
|
938
|
+
const patterns = owned(listBehaviorPatterns())
|
|
939
|
+
.slice(0, 8)
|
|
940
|
+
.map((pattern) => compactOption(pattern, {
|
|
941
|
+
patternId: pattern.id,
|
|
942
|
+
trigger: pattern.title
|
|
943
|
+
}))
|
|
944
|
+
.filter((option) => option.label.length > 0);
|
|
945
|
+
const behaviors = owned(listBehaviors())
|
|
946
|
+
.slice(0, 8)
|
|
947
|
+
.map((behavior) => compactOption(behavior, {
|
|
948
|
+
behaviorId: behavior.id,
|
|
949
|
+
behavior: behavior.title
|
|
950
|
+
}))
|
|
951
|
+
.filter((option) => option.label.length > 0);
|
|
952
|
+
const modes = owned(listModeProfiles())
|
|
953
|
+
.slice(0, 8)
|
|
954
|
+
.map((mode) => compactOption(mode, {
|
|
955
|
+
modeId: mode.id,
|
|
956
|
+
mode: mode.title
|
|
957
|
+
}))
|
|
958
|
+
.filter((option) => option.label.length > 0);
|
|
959
|
+
const recentReports = owned(listTriggerReports(6)).map((report) => ({
|
|
960
|
+
id: report.id,
|
|
961
|
+
title: report.title,
|
|
962
|
+
occurredAt: report.occurredAt,
|
|
963
|
+
status: report.status
|
|
964
|
+
}));
|
|
965
|
+
return {
|
|
966
|
+
emotionOptions,
|
|
967
|
+
triggerOptions,
|
|
968
|
+
routinePromptOptions,
|
|
969
|
+
questions: [
|
|
970
|
+
{
|
|
971
|
+
id: "event",
|
|
972
|
+
title: "What happened?",
|
|
973
|
+
prompt: "Pick the closest event type.",
|
|
974
|
+
eventType: "trigger_capture",
|
|
975
|
+
options: events.length > 0
|
|
976
|
+
? events
|
|
977
|
+
: compactFallbackOptions(triggerOptions.slice(0, 6), "eventTypeLabel")
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
id: "emotion",
|
|
981
|
+
title: "Emotion",
|
|
982
|
+
prompt: "What emotion is present?",
|
|
983
|
+
eventType: "emotion_check_in",
|
|
984
|
+
options: emotions.length > 0
|
|
985
|
+
? emotions
|
|
986
|
+
: compactFallbackOptions(emotionOptions.slice(0, 6), "emotion")
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
id: "pattern",
|
|
990
|
+
title: "Pattern",
|
|
991
|
+
prompt: "Does this match a known loop?",
|
|
992
|
+
eventType: "trigger_capture",
|
|
993
|
+
options: patterns.length > 0
|
|
994
|
+
? patterns
|
|
995
|
+
: compactFallbackOptions(triggerOptions.slice(0, 6), "trigger")
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
id: "outcome",
|
|
999
|
+
title: "Urge outcome",
|
|
1000
|
+
prompt: "What did you do with the urge?",
|
|
1001
|
+
eventType: "trigger_capture",
|
|
1002
|
+
options: compactFallbackOptions(["Resisted", "Indulged", "Delayed", "Repaired"], "outcome")
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
id: "value",
|
|
1006
|
+
title: "Value",
|
|
1007
|
+
prompt: "Which value is this about?",
|
|
1008
|
+
eventType: "mark_moment",
|
|
1009
|
+
options: values.length > 0
|
|
1010
|
+
? values
|
|
1011
|
+
: compactFallbackOptions(["Health", "Work", "Love", "Courage"], "value")
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
id: "mode",
|
|
1015
|
+
title: "Mode",
|
|
1016
|
+
prompt: "Which mode is active?",
|
|
1017
|
+
eventType: "trigger_capture",
|
|
1018
|
+
options: modes.length > 0
|
|
1019
|
+
? modes
|
|
1020
|
+
: compactFallbackOptions(["Protected", "Avoidant", "Driven", "Connected"], "mode")
|
|
1021
|
+
},
|
|
1022
|
+
{
|
|
1023
|
+
id: "behavior",
|
|
1024
|
+
title: "Behavior",
|
|
1025
|
+
prompt: "What behavior showed up?",
|
|
1026
|
+
eventType: "trigger_capture",
|
|
1027
|
+
options: behaviors.length > 0
|
|
1028
|
+
? behaviors
|
|
1029
|
+
: compactFallbackOptions(["Avoided", "Approached", "Scrolled", "Asked"], "behavior")
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
id: "routine",
|
|
1033
|
+
title: "Routine",
|
|
1034
|
+
prompt: "Log one daily signal.",
|
|
1035
|
+
eventType: "routine_check",
|
|
1036
|
+
options: compactFallbackOptions(routinePromptOptions.slice(0, 6), "routine")
|
|
1037
|
+
}
|
|
1038
|
+
],
|
|
1039
|
+
recentReports
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
738
1042
|
function buildWatchSurfaces() {
|
|
739
1043
|
return [
|
|
740
1044
|
{ id: "now", title: "Now", icon: "sparkle" },
|
|
@@ -805,11 +1109,7 @@ export function buildWatchBootstrap(pairing, options) {
|
|
|
805
1109
|
today,
|
|
806
1110
|
health: buildHealthSnapshot(pairing.user_id),
|
|
807
1111
|
movement: buildMovementSnapshot(pairing.user_id),
|
|
808
|
-
psyche:
|
|
809
|
-
emotionOptions,
|
|
810
|
-
triggerOptions,
|
|
811
|
-
routinePromptOptions
|
|
812
|
-
},
|
|
1112
|
+
psyche: buildWatchPsycheSnapshot(pairing),
|
|
813
1113
|
inbox: {
|
|
814
1114
|
prompts: pendingPrompts
|
|
815
1115
|
},
|
|
@@ -856,7 +1156,7 @@ export function ingestWatchCaptureBatch(pairing, input) {
|
|
|
856
1156
|
const receivedAt = nowIso();
|
|
857
1157
|
insert.run(id, pairing.id, pairing.user_id, event.dedupeKey, parsed.device.sourceDevice, event.eventType, event.promptId, event.recordedAt, receivedAt, JSON.stringify(event.linkedContext), JSON.stringify(event.payload), "stored", "{}", receivedAt);
|
|
858
1158
|
storedCount += 1;
|
|
859
|
-
const projection = projectionForStoredEvent(event);
|
|
1159
|
+
const projection = projectionForStoredEvent(pairing, event);
|
|
860
1160
|
updateProjection.run(projection.status, JSON.stringify(projection.details), id);
|
|
861
1161
|
if (projection.status === "projected") {
|
|
862
1162
|
projectedCount += 1;
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "forge-openclaw-plugin",
|
|
3
3
|
"name": "Forge",
|
|
4
4
|
"description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.106",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
|
8
8
|
"onCapabilities": [
|
package/package.json
CHANGED