forge-openclaw-plugin 0.2.60 → 0.2.65

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.
Files changed (59) hide show
  1. package/README.md +121 -51
  2. package/dist/assets/{board-B1V3M__K.js → board-DUwMfZvN.js} +1 -1
  3. package/dist/assets/index-B9VOpR7r.css +1 -0
  4. package/dist/assets/index-DoHjjze2.js +90 -0
  5. package/dist/assets/{motion-CltSTItx.js → motion-Crg3QyXD.js} +1 -1
  6. package/dist/assets/{table-B-VrSFx8.js → table-CTlDeYRs.js} +1 -1
  7. package/dist/assets/{ui-DUqM4jkt.js → ui-CJPaElbj.js} +1 -1
  8. package/dist/assets/{vendor-C0otBhgu.js → vendor-BdrT2htV.js} +217 -207
  9. package/dist/companion-iroh/darwin-arm64/forge-companion-iroh +0 -0
  10. package/dist/companion-iroh/darwin-x64/forge-companion-iroh +0 -0
  11. package/dist/companion-iroh/linux-x64/forge-companion-iroh +0 -0
  12. package/dist/companion-iroh-src/Cargo.lock +4559 -0
  13. package/dist/companion-iroh-src/Cargo.toml +37 -0
  14. package/dist/companion-iroh-src/src/lib.rs +279 -0
  15. package/dist/companion-iroh-src/src/main.rs +478 -0
  16. package/dist/companion-iroh-src/src/protocol.rs +129 -0
  17. package/dist/gamification-previews/dark-fantasy-item-trophy-tasks-anvil-marathon.webp +0 -0
  18. package/dist/gamification-previews/dark-fantasy-item-trophy-xp-levels-the-first-heat.webp +0 -0
  19. package/dist/gamification-previews/dark-fantasy-item-unlock-streaks-molten-crown-fire.webp +0 -0
  20. package/dist/gamification-previews/dark-fantasy-mascot.webp +0 -0
  21. package/dist/gamification-previews/dramatic-smithie-item-trophy-tasks-anvil-marathon.webp +0 -0
  22. package/dist/gamification-previews/dramatic-smithie-item-trophy-xp-levels-the-first-heat.webp +0 -0
  23. package/dist/gamification-previews/dramatic-smithie-item-unlock-streaks-molten-crown-fire.webp +0 -0
  24. package/dist/gamification-previews/dramatic-smithie-mascot.webp +0 -0
  25. package/dist/gamification-previews/mind-locksmith-item-trophy-tasks-anvil-marathon.webp +0 -0
  26. package/dist/gamification-previews/mind-locksmith-item-trophy-xp-levels-the-first-heat.webp +0 -0
  27. package/dist/gamification-previews/mind-locksmith-item-unlock-streaks-molten-crown-fire.webp +0 -0
  28. package/dist/gamification-previews/mind-locksmith-mascot.webp +0 -0
  29. package/dist/index.html +7 -7
  30. package/dist/openclaw/parity.js +27 -0
  31. package/dist/openclaw/plugin-entry-shared.js +2 -2
  32. package/dist/openclaw/plugin-sdk-types.d.ts +2 -1
  33. package/dist/openclaw/routes.d.ts +4 -0
  34. package/dist/openclaw/routes.js +112 -3
  35. package/dist/openclaw/tools.js +32 -4
  36. package/dist/server/server/migrations/059_data_backup_retention.sql +2 -0
  37. package/dist/server/server/src/app.js +288 -61
  38. package/dist/server/server/src/data-management-types.js +2 -0
  39. package/dist/server/server/src/discovery-advertiser.js +13 -0
  40. package/dist/server/server/src/health.js +58 -3
  41. package/dist/server/server/src/movement.js +16 -1
  42. package/dist/server/server/src/openapi.js +410 -9
  43. package/dist/server/server/src/repositories/rewards.js +60 -0
  44. package/dist/server/server/src/services/companion-iroh.js +425 -0
  45. package/dist/server/server/src/services/data-management.js +32 -2
  46. package/dist/server/server/src/services/doctor.js +762 -0
  47. package/dist/server/server/src/services/gamification.js +75 -3
  48. package/dist/server/server/src/services/life-force.js +166 -25
  49. package/dist/server/server/src/web.js +88 -12
  50. package/dist/server/src/lib/api.js +9 -0
  51. package/dist/server/src/lib/gamification-catalog.js +1 -1
  52. package/openclaw.plugin.json +85 -3
  53. package/package.json +10 -6
  54. package/server/migrations/059_data_backup_retention.sql +2 -0
  55. package/skills/forge-openclaw/SKILL.md +80 -19
  56. package/skills/forge-openclaw/entity_conversation_playbooks.md +283 -25
  57. package/skills/forge-openclaw/psyche_entity_playbooks.md +82 -0
  58. package/dist/assets/index-BwKAPo98.css +0 -1
  59. package/dist/assets/index-Dy7c-dRY.js +0 -90
@@ -28,6 +28,13 @@ const HTTP_METHODS = new Set([
28
28
  "options",
29
29
  "head"
30
30
  ]);
31
+ const CALENDAR_PROVIDER_VALUES = [
32
+ "google",
33
+ "apple",
34
+ "microsoft",
35
+ "caldav",
36
+ "macos_local"
37
+ ];
31
38
  const API_TAGS = [
32
39
  {
33
40
  name: "Meta",
@@ -710,7 +717,7 @@ export function buildOpenApiDocument() {
710
717
  ],
711
718
  properties: {
712
719
  id: { type: "string" },
713
- provider: { type: "string", enum: ["google", "apple", "caldav"] },
720
+ provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
714
721
  label: { type: "string" },
715
722
  accountLabel: { type: "string" },
716
723
  status: {
@@ -725,6 +732,134 @@ export function buildOpenApiDocument() {
725
732
  updatedAt: { type: "string", format: "date-time" }
726
733
  }
727
734
  };
735
+ const calendarConnectionMutationInput = {
736
+ type: "object",
737
+ additionalProperties: true,
738
+ required: ["provider", "label"],
739
+ properties: {
740
+ provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
741
+ label: { type: "string" },
742
+ username: { type: "string" },
743
+ password: { type: "string" },
744
+ serverUrl: { type: "string" },
745
+ authSessionId: { type: "string" },
746
+ sourceId: { type: "string" },
747
+ selectedCalendarUrls: arrayOf({ type: "string" }),
748
+ forgeCalendarUrl: nullable({ type: "string" }),
749
+ createForgeCalendar: { type: "boolean" },
750
+ replaceConnectionIds: arrayOf({ type: "string" })
751
+ }
752
+ };
753
+ const calendarConnectionPatchInput = {
754
+ type: "object",
755
+ additionalProperties: false,
756
+ properties: {
757
+ label: { type: "string" },
758
+ selectedCalendarUrls: arrayOf({ type: "string" })
759
+ }
760
+ };
761
+ const calendarDiscoveryInput = {
762
+ type: "object",
763
+ additionalProperties: true,
764
+ required: ["provider"],
765
+ properties: {
766
+ provider: { type: "string", enum: ["apple", "caldav"] },
767
+ serverUrl: { type: "string" },
768
+ username: { type: "string" },
769
+ password: { type: "string" }
770
+ }
771
+ };
772
+ const calendarDiscoveryCalendar = {
773
+ type: "object",
774
+ additionalProperties: true,
775
+ required: [
776
+ "url",
777
+ "displayName",
778
+ "description",
779
+ "color",
780
+ "timezone",
781
+ "isPrimary",
782
+ "canWrite",
783
+ "selectedByDefault",
784
+ "isForgeCandidate"
785
+ ],
786
+ properties: {
787
+ url: { type: "string" },
788
+ displayName: { type: "string" },
789
+ dedupedName: { type: "string" },
790
+ description: { type: "string" },
791
+ color: { type: "string" },
792
+ timezone: { type: "string" },
793
+ isPrimary: { type: "boolean" },
794
+ canWrite: { type: "boolean" },
795
+ selectedByDefault: { type: "boolean" },
796
+ isForgeCandidate: { type: "boolean" },
797
+ sourceId: nullable({ type: "string" }),
798
+ sourceTitle: nullable({ type: "string" }),
799
+ sourceType: nullable({ type: "string" }),
800
+ calendarType: nullable({ type: "string" }),
801
+ hostCalendarId: nullable({ type: "string" }),
802
+ canonicalKey: nullable({ type: "string" })
803
+ }
804
+ };
805
+ const calendarDiscoveryPayload = {
806
+ type: "object",
807
+ additionalProperties: true,
808
+ required: [
809
+ "provider",
810
+ "accountLabel",
811
+ "serverUrl",
812
+ "principalUrl",
813
+ "homeUrl",
814
+ "calendars"
815
+ ],
816
+ properties: {
817
+ provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
818
+ accountLabel: { type: "string" },
819
+ serverUrl: { type: "string" },
820
+ principalUrl: nullable({ type: "string" }),
821
+ homeUrl: nullable({ type: "string" }),
822
+ calendars: arrayOf(calendarDiscoveryCalendar)
823
+ }
824
+ };
825
+ const macOSLocalCalendarDiscoveryPayload = {
826
+ type: "object",
827
+ additionalProperties: true,
828
+ required: ["status", "requestedAt", "sources"],
829
+ properties: {
830
+ status: {
831
+ type: "string",
832
+ enum: [
833
+ "not_determined",
834
+ "denied",
835
+ "restricted",
836
+ "full_access",
837
+ "unavailable"
838
+ ]
839
+ },
840
+ requestedAt: { type: "string", format: "date-time" },
841
+ sources: arrayOf({
842
+ type: "object",
843
+ additionalProperties: true,
844
+ required: [
845
+ "sourceId",
846
+ "sourceTitle",
847
+ "sourceType",
848
+ "accountLabel",
849
+ "accountIdentityKey",
850
+ "calendars"
851
+ ],
852
+ properties: {
853
+ sourceId: { type: "string" },
854
+ sourceTitle: { type: "string" },
855
+ sourceType: { type: "string" },
856
+ accountLabel: { type: "string" },
857
+ accountIdentityKey: { type: "string" },
858
+ calendars: arrayOf(calendarDiscoveryCalendar)
859
+ }
860
+ })
861
+ }
862
+ };
728
863
  const calendarResource = {
729
864
  type: "object",
730
865
  additionalProperties: false,
@@ -781,7 +916,7 @@ export function buildOpenApiDocument() {
781
916
  ],
782
917
  properties: {
783
918
  id: { type: "string" },
784
- provider: { type: "string", enum: ["google", "apple", "caldav"] },
919
+ provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
785
920
  connectionId: nullable({ type: "string" }),
786
921
  calendarId: nullable({ type: "string" }),
787
922
  remoteCalendarId: nullable({ type: "string" }),
@@ -3218,6 +3353,95 @@ export function buildOpenApiDocument() {
3218
3353
  agentTokens: arrayOf({ $ref: "#/components/schemas/AgentTokenSummary" })
3219
3354
  }
3220
3355
  };
3356
+ const doctorFixProposal = {
3357
+ type: "object",
3358
+ additionalProperties: false,
3359
+ required: ["id", "kind", "title", "description", "requiresConfirmation"],
3360
+ properties: {
3361
+ id: { type: "string" },
3362
+ kind: { type: "string", enum: ["manual", "safe_auto_fix"] },
3363
+ title: { type: "string" },
3364
+ description: { type: "string" },
3365
+ requiresConfirmation: { type: "boolean" }
3366
+ }
3367
+ };
3368
+ const doctorCheck = {
3369
+ type: "object",
3370
+ additionalProperties: false,
3371
+ required: [
3372
+ "id",
3373
+ "group",
3374
+ "title",
3375
+ "status",
3376
+ "severity",
3377
+ "summary",
3378
+ "evidence",
3379
+ "affectedCount"
3380
+ ],
3381
+ properties: {
3382
+ id: { type: "string" },
3383
+ group: { type: "string" },
3384
+ title: { type: "string" },
3385
+ status: { type: "string", enum: ["pass", "warn", "fail", "skipped"] },
3386
+ severity: { type: "string", enum: ["info", "warning", "error"] },
3387
+ summary: { type: "string" },
3388
+ evidence: arrayOf({ type: "string" }),
3389
+ affectedCount: { type: "integer" },
3390
+ fix: { $ref: "#/components/schemas/DoctorFixProposal" }
3391
+ }
3392
+ };
3393
+ const forgeDoctorReport = {
3394
+ type: "object",
3395
+ additionalProperties: true,
3396
+ required: [
3397
+ "ok",
3398
+ "now",
3399
+ "integrity",
3400
+ "runtime",
3401
+ "health",
3402
+ "settingsFile",
3403
+ "settingsSummary",
3404
+ "checks",
3405
+ "issues",
3406
+ "fixProposals",
3407
+ "warnings"
3408
+ ],
3409
+ properties: {
3410
+ ok: { type: "boolean" },
3411
+ now: { type: "string", format: "date-time" },
3412
+ integrity: {
3413
+ type: "object",
3414
+ additionalProperties: true,
3415
+ required: ["score", "status", "headline", "lastCheckedAt"],
3416
+ properties: {
3417
+ score: { type: "integer" },
3418
+ status: { type: "string", enum: ["healthy", "warning", "critical"] },
3419
+ headline: { type: "string" },
3420
+ lastCheckedAt: { type: "string", format: "date-time" }
3421
+ }
3422
+ },
3423
+ runtime: { type: "object", additionalProperties: true },
3424
+ health: { type: "object", additionalProperties: true },
3425
+ settingsFile: { type: "object", additionalProperties: true },
3426
+ settingsSummary: { type: "object", additionalProperties: true },
3427
+ checks: arrayOf({ $ref: "#/components/schemas/DoctorCheck" }),
3428
+ issues: arrayOf({ $ref: "#/components/schemas/DoctorCheck" }),
3429
+ fixProposals: arrayOf({
3430
+ $ref: "#/components/schemas/DoctorFixProposal"
3431
+ }),
3432
+ warnings: arrayOf({ type: "string" })
3433
+ }
3434
+ };
3435
+ const doctorFixResult = {
3436
+ type: "object",
3437
+ additionalProperties: false,
3438
+ required: ["fixId", "status", "summary"],
3439
+ properties: {
3440
+ fixId: { type: "string" },
3441
+ status: { type: "string", enum: ["applied", "skipped", "failed"] },
3442
+ summary: { type: "string" }
3443
+ }
3444
+ };
3221
3445
  const agentOnboardingPayload = {
3222
3446
  type: "object",
3223
3447
  additionalProperties: false,
@@ -3527,7 +3751,9 @@ export function buildOpenApiDocument() {
3527
3751
  additionalProperties: false,
3528
3752
  required: [
3529
3753
  "classification",
3754
+ "aliases",
3530
3755
  "summary",
3756
+ "methodRoutes",
3531
3757
  "readRoutes",
3532
3758
  "writeRoutes",
3533
3759
  "routeSelectionQuestions",
@@ -3538,7 +3764,12 @@ export function buildOpenApiDocument() {
3538
3764
  type: "string",
3539
3765
  enum: ["specialized_domain_surface"]
3540
3766
  },
3767
+ aliases: arrayOf({ type: "string" }),
3541
3768
  summary: { type: "string" },
3769
+ methodRoutes: {
3770
+ type: "object",
3771
+ additionalProperties: { type: "string" }
3772
+ },
3542
3773
  readRoutes: {
3543
3774
  type: "object",
3544
3775
  additionalProperties: { type: "string" }
@@ -3859,7 +4090,8 @@ export function buildOpenApiDocument() {
3859
4090
  "specializedRouteToolRule",
3860
4091
  "createExample",
3861
4092
  "updateExample",
3862
- "specializedRouteToolExample"
4093
+ "specializedRouteToolExample",
4094
+ "specializedRouteToolExamples"
3863
4095
  ],
3864
4096
  properties: {
3865
4097
  preferredBatchRoutes: {
@@ -3885,7 +4117,11 @@ export function buildOpenApiDocument() {
3885
4117
  specializedRouteToolRule: { type: "string" },
3886
4118
  createExample: { type: "string" },
3887
4119
  updateExample: { type: "string" },
3888
- specializedRouteToolExample: { type: "string" }
4120
+ specializedRouteToolExample: { type: "string" },
4121
+ specializedRouteToolExamples: {
4122
+ type: "object",
4123
+ additionalProperties: { type: "string" }
4124
+ }
3889
4125
  }
3890
4126
  }
3891
4127
  }
@@ -4674,6 +4910,11 @@ export function buildOpenApiDocument() {
4674
4910
  Project: project,
4675
4911
  CalendarSchedulingRules: calendarSchedulingRules,
4676
4912
  CalendarConnection: calendarConnection,
4913
+ CalendarConnectionMutationInput: calendarConnectionMutationInput,
4914
+ CalendarConnectionPatchInput: calendarConnectionPatchInput,
4915
+ CalendarDiscoveryInput: calendarDiscoveryInput,
4916
+ CalendarDiscoveryPayload: calendarDiscoveryPayload,
4917
+ MacOSLocalCalendarDiscoveryPayload: macOSLocalCalendarDiscoveryPayload,
4677
4918
  CalendarResource: calendarResource,
4678
4919
  CalendarEventSource: calendarEventSource,
4679
4920
  CalendarEventLink: calendarEventLink,
@@ -4703,6 +4944,10 @@ export function buildOpenApiDocument() {
4703
4944
  InsightsPayload: insightsPayload,
4704
4945
  WeeklyReviewPayload: weeklyReviewPayload,
4705
4946
  SettingsPayload: settingsPayload,
4947
+ DoctorFixProposal: doctorFixProposal,
4948
+ DoctorCheck: doctorCheck,
4949
+ ForgeDoctorReport: forgeDoctorReport,
4950
+ DoctorFixResult: doctorFixResult,
4706
4951
  ExecutionSettings: executionSettings,
4707
4952
  TaskRunClaimInput: taskRunClaimInput,
4708
4953
  TaskRunHeartbeatInput: taskRunHeartbeatInput,
@@ -4812,6 +5057,52 @@ export function buildOpenApiDocument() {
4812
5057
  }
4813
5058
  }
4814
5059
  },
5060
+ "/api/v1/doctor": {
5061
+ get: {
5062
+ summary: "Run Forge Doctor diagnostics for runtime, settings, storage, entities, hierarchy, rewards, and fix proposals",
5063
+ responses: {
5064
+ "200": jsonResponse({
5065
+ type: "object",
5066
+ required: ["doctor"],
5067
+ properties: {
5068
+ doctor: { $ref: "#/components/schemas/ForgeDoctorReport" }
5069
+ }
5070
+ }, "Forge Doctor report")
5071
+ }
5072
+ }
5073
+ },
5074
+ "/api/v1/doctor/fixes": {
5075
+ post: {
5076
+ summary: "Apply explicitly requested safe Forge Doctor fixes",
5077
+ requestBody: {
5078
+ required: true,
5079
+ content: {
5080
+ "application/json": {
5081
+ schema: {
5082
+ type: "object",
5083
+ additionalProperties: false,
5084
+ properties: {
5085
+ fixIds: arrayOf({ type: "string" }),
5086
+ applyAllSafe: { type: "boolean" }
5087
+ }
5088
+ }
5089
+ }
5090
+ }
5091
+ },
5092
+ responses: {
5093
+ "200": jsonResponse({
5094
+ type: "object",
5095
+ required: ["results", "doctor"],
5096
+ properties: {
5097
+ results: arrayOf({
5098
+ $ref: "#/components/schemas/DoctorFixResult"
5099
+ }),
5100
+ doctor: { $ref: "#/components/schemas/ForgeDoctorReport" }
5101
+ }
5102
+ }, "Forge Doctor fix result")
5103
+ }
5104
+ }
5105
+ },
4815
5106
  "/api/v1/health/sleep": {
4816
5107
  get: {
4817
5108
  summary: "Read the Forge sleep overview surface",
@@ -7555,6 +7846,50 @@ export function buildOpenApiDocument() {
7555
7846
  }
7556
7847
  }
7557
7848
  },
7849
+ "/api/v1/calendar/macos-local/discovery": {
7850
+ get: {
7851
+ summary: "Discover calendars already configured on this Mac through EventKit",
7852
+ responses: {
7853
+ "200": jsonResponse({
7854
+ type: "object",
7855
+ required: ["discovery"],
7856
+ properties: {
7857
+ discovery: {
7858
+ $ref: "#/components/schemas/MacOSLocalCalendarDiscoveryPayload"
7859
+ }
7860
+ }
7861
+ }, "macOS local calendar discovery"),
7862
+ default: { $ref: "#/components/responses/Error" }
7863
+ }
7864
+ }
7865
+ },
7866
+ "/api/v1/calendar/discovery": {
7867
+ post: {
7868
+ summary: "Discover Apple or custom CalDAV calendars before creating a connection",
7869
+ requestBody: {
7870
+ required: true,
7871
+ content: {
7872
+ "application/json": {
7873
+ schema: {
7874
+ $ref: "#/components/schemas/CalendarDiscoveryInput"
7875
+ }
7876
+ }
7877
+ }
7878
+ },
7879
+ responses: {
7880
+ "200": jsonResponse({
7881
+ type: "object",
7882
+ required: ["discovery"],
7883
+ properties: {
7884
+ discovery: {
7885
+ $ref: "#/components/schemas/CalendarDiscoveryPayload"
7886
+ }
7887
+ }
7888
+ }, "Calendar discovery"),
7889
+ default: { $ref: "#/components/responses/Error" }
7890
+ }
7891
+ }
7892
+ },
7558
7893
  "/api/v1/calendar/connections": {
7559
7894
  get: {
7560
7895
  summary: "List connected calendar providers",
@@ -7573,10 +7908,7 @@ export function buildOpenApiDocument() {
7573
7908
  "connectionHelp"
7574
7909
  ],
7575
7910
  properties: {
7576
- provider: {
7577
- type: "string",
7578
- enum: ["google", "apple", "caldav"]
7579
- },
7911
+ provider: { type: "string", enum: CALENDAR_PROVIDER_VALUES },
7580
7912
  label: { type: "string" },
7581
7913
  supportsDedicatedForgeCalendar: { type: "boolean" },
7582
7914
  connectionHelp: { type: "string" }
@@ -7591,8 +7923,18 @@ export function buildOpenApiDocument() {
7591
7923
  }
7592
7924
  },
7593
7925
  post: {
7594
- summary: "Create a Google, Apple, or custom CalDAV calendar connection",
7926
+ summary: "Create a Google, Apple, Exchange Online, local Mac, or custom CalDAV calendar connection",
7595
7927
  description: "Forge first discovers the writable calendars for the account, then stores the chosen mirrored calendars and either reuses the existing shared Forge write target or saves a new one when needed.",
7928
+ requestBody: {
7929
+ required: true,
7930
+ content: {
7931
+ "application/json": {
7932
+ schema: {
7933
+ $ref: "#/components/schemas/CalendarConnectionMutationInput"
7934
+ }
7935
+ }
7936
+ }
7937
+ },
7596
7938
  responses: {
7597
7939
  "201": jsonResponse({
7598
7940
  type: "object",
@@ -7607,6 +7949,65 @@ export function buildOpenApiDocument() {
7607
7949
  }
7608
7950
  }
7609
7951
  },
7952
+ "/api/v1/calendar/connections/{id}": {
7953
+ patch: {
7954
+ summary: "Update one calendar connection label or selected mirrored calendars",
7955
+ requestBody: {
7956
+ required: true,
7957
+ content: {
7958
+ "application/json": {
7959
+ schema: {
7960
+ $ref: "#/components/schemas/CalendarConnectionPatchInput"
7961
+ }
7962
+ }
7963
+ }
7964
+ },
7965
+ responses: {
7966
+ "200": jsonResponse({
7967
+ type: "object",
7968
+ required: ["connection"],
7969
+ properties: {
7970
+ connection: {
7971
+ $ref: "#/components/schemas/CalendarConnection"
7972
+ }
7973
+ }
7974
+ }, "Updated calendar connection"),
7975
+ default: { $ref: "#/components/responses/Error" }
7976
+ }
7977
+ },
7978
+ delete: {
7979
+ summary: "Delete one calendar connection and stop mirroring it",
7980
+ responses: {
7981
+ "200": jsonResponse({
7982
+ type: "object",
7983
+ required: ["connection"],
7984
+ properties: {
7985
+ connection: {
7986
+ $ref: "#/components/schemas/CalendarConnection"
7987
+ }
7988
+ }
7989
+ }, "Deleted calendar connection"),
7990
+ default: { $ref: "#/components/responses/Error" }
7991
+ }
7992
+ }
7993
+ },
7994
+ "/api/v1/calendar/connections/{id}/discovery": {
7995
+ get: {
7996
+ summary: "Rediscover available calendars for an existing calendar connection",
7997
+ responses: {
7998
+ "200": jsonResponse({
7999
+ type: "object",
8000
+ required: ["discovery"],
8001
+ properties: {
8002
+ discovery: {
8003
+ $ref: "#/components/schemas/CalendarDiscoveryPayload"
8004
+ }
8005
+ }
8006
+ }, "Calendar connection discovery"),
8007
+ default: { $ref: "#/components/responses/Error" }
8008
+ }
8009
+ }
8010
+ },
7610
8011
  "/api/v1/calendar/connections/{id}/sync": {
7611
8012
  post: {
7612
8013
  summary: "Sync one connected calendar provider",
@@ -27,6 +27,14 @@ const DEFAULT_RULES = [
27
27
  description: "Award a small XP bounty for each ten credited minutes of active work.",
28
28
  config: { fixedXp: 4, intervalMinutes: 10 }
29
29
  },
30
+ {
31
+ id: "reward_rule_entity_created",
32
+ family: "consistency",
33
+ code: "entity_created",
34
+ title: "Forge entity created",
35
+ description: "Award a small activity bounty when the user creates a real Forge entity.",
36
+ config: { fixedXp: 2 }
37
+ },
30
38
  {
31
39
  id: "reward_rule_task_run_completion",
32
40
  family: "completion",
@@ -673,6 +681,58 @@ export function recordTaskRunProgressRewards(taskRunId, taskId, actor, source, c
673
681
  }
674
682
  return rewards;
675
683
  }
684
+ export function recordEntityCreationReward(input) {
685
+ ensureDefaultRewardRules();
686
+ const reversibleGroup = `entity_created:${input.entityType}:${input.entityId}`;
687
+ const existing = getDatabase()
688
+ .prepare(`SELECT
689
+ id, rule_id, event_log_id, entity_type, entity_id, actor, source,
690
+ delta_xp, reason_title, reason_summary, reversible_group,
691
+ reversed_by_reward_id, metadata_json, created_at
692
+ FROM reward_ledger
693
+ WHERE reversible_group = ?
694
+ ORDER BY created_at ASC
695
+ LIMIT 1`)
696
+ .get(reversibleGroup);
697
+ if (existing) {
698
+ return mapLedger(existing);
699
+ }
700
+ const createdAtDate = Number.isNaN(Date.parse(input.createdAt))
701
+ ? new Date()
702
+ : new Date(input.createdAt);
703
+ const rule = getRuleByCode("entity_created");
704
+ const readableType = input.entityType.replaceAll("_", " ");
705
+ const title = input.title?.trim() || readableType;
706
+ const eventLog = recordEventLog({
707
+ eventKind: "reward.entity_created",
708
+ entityType: input.entityType,
709
+ entityId: input.entityId,
710
+ actor: input.actor ?? null,
711
+ source: input.source ?? "system",
712
+ metadata: {
713
+ entityType: input.entityType,
714
+ entityId: input.entityId,
715
+ title,
716
+ createdAt: input.createdAt
717
+ }
718
+ }, createdAtDate);
719
+ return insertLedgerEvent({
720
+ ruleId: rule?.id ?? null,
721
+ eventLogId: eventLog.id,
722
+ entityType: input.entityType,
723
+ entityId: input.entityId,
724
+ actor: input.actor ?? null,
725
+ source: input.source ?? "system",
726
+ deltaXp: Number(rule?.config.fixedXp ?? 2),
727
+ reasonTitle: `Created ${readableType}: ${title}`,
728
+ reasonSummary: "Small Forge activity XP for creating something concrete.",
729
+ reversibleGroup,
730
+ metadata: {
731
+ entityType: input.entityType,
732
+ title
733
+ }
734
+ }, createdAtDate);
735
+ }
676
736
  export function recordWorkAdjustmentReward(input) {
677
737
  const { rule, intervalMinutes, intervalSeconds, fixedXp } = getTaskRunProgressRewardCadence();
678
738
  const entityType = workAdjustmentEntityTypeSchema.parse(input.entityType);