forge-openclaw-plugin 0.2.27 → 0.2.29
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/README.md +2 -1
- package/dist/assets/{board-C6jCchjI.js → board-q8cfwaAW.js} +2 -2
- package/dist/assets/{board-C6jCchjI.js.map → board-q8cfwaAW.js.map} +1 -1
- package/dist/assets/index-C6PCeHD_.css +1 -0
- package/dist/assets/index-bfHIqj0-.js +85 -0
- package/dist/assets/index-bfHIqj0-.js.map +1 -0
- package/dist/assets/{motion-DFHrH2rd.js → motion-DHfqFntt.js} +2 -2
- package/dist/assets/{motion-DFHrH2rd.js.map → motion-DHfqFntt.js.map} +1 -1
- package/dist/assets/{table-ZL7Di_u3.js → table-DLweENXt.js} +2 -2
- package/dist/assets/{table-ZL7Di_u3.js.map → table-DLweENXt.js.map} +1 -1
- package/dist/assets/{ui-CKNPpz7q.js → ui-BV0OYxkH.js} +2 -2
- package/dist/assets/{ui-CKNPpz7q.js.map → ui-BV0OYxkH.js.map} +1 -1
- package/dist/assets/{vendor-DoNZuFhn.js → vendor-OwcH20PM.js} +204 -204
- package/dist/assets/vendor-OwcH20PM.js.map +1 -0
- package/dist/index.html +7 -7
- package/dist/server/server/migrations/044_macos_local_calendar_provider.sql +21 -0
- package/dist/server/server/src/app.js +331 -14
- package/dist/server/server/src/openapi.js +828 -3
- package/dist/server/server/src/repositories/calendar.js +295 -12
- package/dist/server/server/src/repositories/tasks.js +36 -17
- package/dist/server/server/src/services/calendar-runtime.js +613 -32
- package/dist/server/server/src/services/life-force-model.js +20 -0
- package/dist/server/server/src/services/life-force.js +1333 -97
- package/dist/server/server/src/services/macos-calendar-helper.js +748 -0
- package/dist/server/server/src/types.js +67 -3
- package/dist/server/src/lib/api-error.js +2 -0
- package/dist/server/src/lib/api.js +39 -2
- package/dist/server/src/lib/calendar-name-deduper.js +2 -0
- package/dist/server/src/lib/snapshot-normalizer.js +2 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/044_macos_local_calendar_provider.sql +21 -0
- package/skills/forge-openclaw/SKILL.md +38 -5
- package/skills/forge-openclaw/entity_conversation_playbooks.md +326 -5
- package/skills/forge-openclaw/psyche_entity_playbooks.md +57 -0
- package/dist/assets/index-DVvS8iiU.css +0 -1
- package/dist/assets/index-zYB-9Dfo.js +0 -85
- package/dist/assets/index-zYB-9Dfo.js.map +0 -1
- package/dist/assets/vendor-DoNZuFhn.js.map +0 -1
package/dist/index.html
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
/>
|
|
14
14
|
<link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
15
15
|
<link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
16
|
-
<script type="module" crossorigin src="/forge/assets/index-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/board-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/ui-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/motion-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/table-
|
|
16
|
+
<script type="module" crossorigin src="/forge/assets/index-bfHIqj0-.js"></script>
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-OwcH20PM.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/board-q8cfwaAW.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/ui-BV0OYxkH.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/motion-DHfqFntt.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/table-DLweENXt.js">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-DT3pnAKJ.css">
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/index-
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/index-C6PCeHD_.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body class="bg-canvas text-ink antialiased">
|
|
26
26
|
<div id="root"></div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
ALTER TABLE calendar_calendars
|
|
2
|
+
ADD COLUMN source_id TEXT;
|
|
3
|
+
|
|
4
|
+
ALTER TABLE calendar_calendars
|
|
5
|
+
ADD COLUMN source_title TEXT;
|
|
6
|
+
|
|
7
|
+
ALTER TABLE calendar_calendars
|
|
8
|
+
ADD COLUMN source_type TEXT;
|
|
9
|
+
|
|
10
|
+
ALTER TABLE calendar_calendars
|
|
11
|
+
ADD COLUMN calendar_type TEXT;
|
|
12
|
+
|
|
13
|
+
ALTER TABLE calendar_calendars
|
|
14
|
+
ADD COLUMN host_calendar_id TEXT;
|
|
15
|
+
|
|
16
|
+
ALTER TABLE calendar_calendars
|
|
17
|
+
ADD COLUMN canonical_key TEXT;
|
|
18
|
+
|
|
19
|
+
UPDATE calendar_calendars
|
|
20
|
+
SET canonical_key = remote_id
|
|
21
|
+
WHERE canonical_key IS NULL OR TRIM(canonical_key) = '';
|
|
@@ -48,7 +48,7 @@ import { getWeeklyReviewPayload } from "./services/reviews.js";
|
|
|
48
48
|
import { finalizeWeeklyReviewClosure } from "./repositories/weekly-reviews.js";
|
|
49
49
|
import { createTaskRunWatchdog } from "./services/task-run-watchdog.js";
|
|
50
50
|
import { suggestTags } from "./services/tagging.js";
|
|
51
|
-
import { CalendarConnectionConflictError, completeGoogleCalendarOauth, completeMicrosoftCalendarOauth, createCalendarConnection, deleteCalendarEventProjection, discoverCalendarConnection, discoverExistingCalendarConnection, getGoogleCalendarOauthSession, getMicrosoftCalendarOauthSession, listConnectedCalendarConnections, removeCalendarConnection, pushCalendarEventUpdate, readCalendarOverview, syncCalendarConnection, startGoogleCalendarOauth, startMicrosoftCalendarOauth, testMicrosoftCalendarOauthConfiguration, listCalendarProviderMetadata, updateCalendarConnectionSelection } from "./services/calendar-runtime.js";
|
|
51
|
+
import { CalendarConnectionConflictError, CalendarConnectionOverlapError, completeGoogleCalendarOauth, completeMicrosoftCalendarOauth, createCalendarConnection, discoverMacOSLocalCalendarSources, deleteCalendarEventProjection, discoverCalendarConnection, discoverExistingCalendarConnection, getGoogleCalendarOauthSession, getMacOSLocalCalendarAccessStatus, getMicrosoftCalendarOauthSession, listConnectedCalendarConnections, removeCalendarConnection, requestMacOSLocalCalendarAccess, pushCalendarEventUpdate, readCalendarOverview, syncCalendarConnection, startGoogleCalendarOauth, startMicrosoftCalendarOauth, testMicrosoftCalendarOauthConfiguration, listCalendarProviderMetadata, updateCalendarConnectionSelection } from "./services/calendar-runtime.js";
|
|
52
52
|
import { consumeOpenAiCodexOauthCredentials, getOpenAiCodexOauthSession, startOpenAiCodexOauthSession, submitOpenAiCodexOauthManualInput } from "./services/openai-codex-oauth.js";
|
|
53
53
|
import { PSYCHE_ENTITY_TYPES, createBehaviorSchema, createBeliefEntrySchema, createBehaviorPatternSchema, createEmotionDefinitionSchema, createEventTypeSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateBehaviorPatternSchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "./psyche-types.js";
|
|
54
54
|
import { createQuestionnaireInstrumentSchema, publishQuestionnaireVersionSchema, startQuestionnaireRunSchema, updateQuestionnaireRunSchema, updateQuestionnaireVersionSchema } from "./questionnaire-types.js";
|
|
@@ -2530,6 +2530,19 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2530
2530
|
],
|
|
2531
2531
|
fieldGuide: []
|
|
2532
2532
|
}),
|
|
2533
|
+
enrichOnboardingEntityGuide({
|
|
2534
|
+
entityType: "work_adjustment",
|
|
2535
|
+
purpose: "A truthful signed minute correction on an existing task or project when work happened outside a live run.",
|
|
2536
|
+
minimumCreateFields: [],
|
|
2537
|
+
relationshipRules: [
|
|
2538
|
+
"Work adjustments are action-heavy corrections, not normal CRUD entities.",
|
|
2539
|
+
"Use forge_adjust_work_minutes when the target task or project already exists and only tracked minutes need to change."
|
|
2540
|
+
],
|
|
2541
|
+
searchHints: [
|
|
2542
|
+
"Confirm the target task or project first, then apply only the signed minute delta that is actually true."
|
|
2543
|
+
],
|
|
2544
|
+
fieldGuide: []
|
|
2545
|
+
}),
|
|
2533
2546
|
enrichOnboardingEntityGuide({
|
|
2534
2547
|
entityType: "questionnaire_run",
|
|
2535
2548
|
purpose: "One user-owned answer session against a questionnaire instrument version.",
|
|
@@ -2543,6 +2556,32 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2543
2556
|
],
|
|
2544
2557
|
fieldGuide: []
|
|
2545
2558
|
}),
|
|
2559
|
+
enrichOnboardingEntityGuide({
|
|
2560
|
+
entityType: "preference_judgment",
|
|
2561
|
+
purpose: "One pairwise preference outcome between two items inside a domain and context.",
|
|
2562
|
+
minimumCreateFields: [],
|
|
2563
|
+
relationshipRules: [
|
|
2564
|
+
"Preference judgments are action-heavy records, not batch CRUD entities.",
|
|
2565
|
+
"Use the dedicated judgment route so the profile, evidence, and comparison history stay aligned."
|
|
2566
|
+
],
|
|
2567
|
+
searchHints: [
|
|
2568
|
+
"Confirm the left and right items, the outcome, and the active context before storing a new judgment."
|
|
2569
|
+
],
|
|
2570
|
+
fieldGuide: []
|
|
2571
|
+
}),
|
|
2572
|
+
enrichOnboardingEntityGuide({
|
|
2573
|
+
entityType: "preference_signal",
|
|
2574
|
+
purpose: "One direct preference signal such as favorite, veto, bookmark, neutral, or compare-later.",
|
|
2575
|
+
minimumCreateFields: [],
|
|
2576
|
+
relationshipRules: [
|
|
2577
|
+
"Preference signals are action-heavy records, not batch CRUD entities.",
|
|
2578
|
+
"Use the dedicated signal route so the profile and evidence model stay aligned."
|
|
2579
|
+
],
|
|
2580
|
+
searchHints: [
|
|
2581
|
+
"Confirm the item, signal type, and context before storing a new direct signal."
|
|
2582
|
+
],
|
|
2583
|
+
fieldGuide: []
|
|
2584
|
+
}),
|
|
2546
2585
|
enrichOnboardingEntityGuide({
|
|
2547
2586
|
entityType: "calendar_connection",
|
|
2548
2587
|
purpose: "A stored external calendar provider connection and its selected calendars.",
|
|
@@ -2582,6 +2621,45 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2582
2621
|
}
|
|
2583
2622
|
]
|
|
2584
2623
|
}),
|
|
2624
|
+
enrichOnboardingEntityGuide({
|
|
2625
|
+
entityType: "movement",
|
|
2626
|
+
purpose: "The specialized Movement surface for day, month, all-time, timeline, trip, place, selection, and manual overlay work.",
|
|
2627
|
+
minimumCreateFields: [],
|
|
2628
|
+
relationshipRules: [
|
|
2629
|
+
"Movement is a specialized domain surface, not a normal batch CRUD entity family.",
|
|
2630
|
+
"Read and mutate it through the dedicated movement routes published under specializedDomainSurfaces."
|
|
2631
|
+
],
|
|
2632
|
+
searchHints: [
|
|
2633
|
+
"Clarify whether the user wants a behavioral query, one trip or place, a missing-gap overlay, a manual add or update, or a link before choosing the route."
|
|
2634
|
+
],
|
|
2635
|
+
fieldGuide: []
|
|
2636
|
+
}),
|
|
2637
|
+
enrichOnboardingEntityGuide({
|
|
2638
|
+
entityType: "life_force",
|
|
2639
|
+
purpose: "The specialized Life Force surface for the current energy overview, profile edits, weekday templates, and fatigue signals.",
|
|
2640
|
+
minimumCreateFields: [],
|
|
2641
|
+
relationshipRules: [
|
|
2642
|
+
"Life Force is a specialized domain surface, not a normal batch CRUD entity family.",
|
|
2643
|
+
"Use the dedicated overview, profile, weekday-template, and fatigue-signal routes."
|
|
2644
|
+
],
|
|
2645
|
+
searchHints: [
|
|
2646
|
+
"Clarify whether the user wants explanation, durable model changes, or a real-time tired or recovered signal before choosing the route."
|
|
2647
|
+
],
|
|
2648
|
+
fieldGuide: []
|
|
2649
|
+
}),
|
|
2650
|
+
enrichOnboardingEntityGuide({
|
|
2651
|
+
entityType: "workbench",
|
|
2652
|
+
purpose: "The specialized Workbench surface for flow catalog work, flow CRUD, execution, run history, published outputs, node results, and latest-node-output reads.",
|
|
2653
|
+
minimumCreateFields: [],
|
|
2654
|
+
relationshipRules: [
|
|
2655
|
+
"Workbench is a specialized execution surface, not a normal batch CRUD entity family.",
|
|
2656
|
+
"Use the dedicated workbench flow, run, output, and node-result routes."
|
|
2657
|
+
],
|
|
2658
|
+
searchHints: [
|
|
2659
|
+
"Clarify whether the user wants flow discovery, editing, execution, published output, run inspection, or node-level output before choosing the route."
|
|
2660
|
+
],
|
|
2661
|
+
fieldGuide: []
|
|
2662
|
+
}),
|
|
2585
2663
|
enrichOnboardingEntityGuide({
|
|
2586
2664
|
entityType: "self_observation",
|
|
2587
2665
|
purpose: "The note-backed Psyche self-observation calendar surface for observed events and reflections.",
|
|
@@ -2625,13 +2703,17 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2625
2703
|
const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
2626
2704
|
"Ask only for what is missing or unclear instead of walking the user through every optional field.",
|
|
2627
2705
|
"Start by saying what seems to matter here or what the record is becoming, then ask the next useful question.",
|
|
2706
|
+
"Whenever possible, make the direction of the intake visible before the question by naming what you think the user is trying to preserve, clarify, decide, schedule, or make easier.",
|
|
2707
|
+
"When the user's operation is not already explicit, identify the job first: add, update, review, compare, navigate, link, or run.",
|
|
2628
2708
|
"Before each question, decide the one missing thing you are trying to clarify and why it matters for the record.",
|
|
2629
2709
|
"Use a progression of concrete example or intent, working name, purpose or meaning, placement in Forge, operational details, and linked context.",
|
|
2630
2710
|
"Ask one to three focused questions at a time. One is usually best when the user is uncertain or emotionally loaded.",
|
|
2631
2711
|
"One focused question is the default. Only stack a second question when both serve the same clarification job and the user is steady enough for it.",
|
|
2712
|
+
"When the user wants review, comparison, or navigation around an existing record, ask what they are trying to understand first and route to the read path before reopening create or update intake.",
|
|
2632
2713
|
"If the user already answered the normal opening question, do not repeat it. Move to the next missing clarification.",
|
|
2633
2714
|
"Do not over-therapize logistical entities. For tasks, calendar events, work blocks, timeboxes, and task runs, one brief confirming sentence plus one question is usually enough.",
|
|
2634
2715
|
"After each substantive answer, briefly say what is becoming clearer and ask only for the next thing that still changes the record shape or usefulness.",
|
|
2716
|
+
"For strategic, reflective, or emotionally meaningful non-Psyche records, ask what feels important to keep true before you ask for labels, dates, or taxonomy.",
|
|
2635
2717
|
"For reusable records such as tags, event types, emotion definitions, preference contexts, or questionnaires, ask what distinction or decision the record should help with before you ask for wording.",
|
|
2636
2718
|
"When useful, help the user name, define, and connect the record in that order: offer a working label, clarify what belongs inside it, then ask about links only after the record itself feels steady.",
|
|
2637
2719
|
"When the meaning is clearer than the wording, offer a tentative title or formulation yourself and invite correction instead of forcing the user to wordsmith alone.",
|
|
@@ -2639,7 +2721,9 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
|
2639
2721
|
"Once the record is clear enough to name, stop exploring broadly and ask only for the last structural detail that still matters.",
|
|
2640
2722
|
"If the record is already clear enough to save, save it instead of performing a ceremonial extra question.",
|
|
2641
2723
|
"If the user accepts the wording or record shape, move to the write instead of reopening the intake.",
|
|
2642
|
-
"When updating an entity, start with what is changing, what should stay true, and what prompted the update now."
|
|
2724
|
+
"When updating an entity, start with what is changing, what should stay true, and what prompted the update now.",
|
|
2725
|
+
"For action-heavy flows such as work adjustments, preference judgments, preference signals, and specialized Movement, Life Force, or Workbench work, first ask what the user is trying to understand, change, add, update, link, or run, then choose the dedicated action or surface route instead of forcing the request into generic CRUD.",
|
|
2726
|
+
"For Movement specifically, treat missing-data corrections as user-defined overlay boxes unless the user is editing an already-recorded stay or trip. When the user already gave a clear instruction like 'that missing block was home', act after only the last ambiguity is resolved."
|
|
2643
2727
|
];
|
|
2644
2728
|
const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
2645
2729
|
{
|
|
@@ -2662,6 +2746,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2662
2746
|
"Ask what this piece of work is trying to make true.",
|
|
2663
2747
|
"Reflect the emerging boundary so the user can hear what is in scope.",
|
|
2664
2748
|
"Ask what outcome would make the project feel real or complete for now.",
|
|
2749
|
+
"Ask what belongs inside the boundary and what can stay out if the scope still feels muddy.",
|
|
2665
2750
|
"Ask which goal it belongs under.",
|
|
2666
2751
|
"Land on a working name once the scope is clear.",
|
|
2667
2752
|
"Clarify status, owner, and notes only after the scope is clear."
|
|
@@ -2696,6 +2781,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2696
2781
|
askSequence: [
|
|
2697
2782
|
"Ask what the recurring behavior is in plain language.",
|
|
2698
2783
|
"Ask whether doing it is aligned or a slip.",
|
|
2784
|
+
"Ask what an honest hit or miss would look like in an ordinary week.",
|
|
2699
2785
|
"Ask about cadence and what counts as an honest check-in in practice.",
|
|
2700
2786
|
"Ask about links only if they will help later review."
|
|
2701
2787
|
]
|
|
@@ -2717,6 +2803,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2717
2803
|
coachingGoal: "Preserve the useful context and link it to the right places without turning the note into a dump.",
|
|
2718
2804
|
askSequence: [
|
|
2719
2805
|
"Ask what the note needs to preserve.",
|
|
2806
|
+
"Ask what sentence future-you would need to recover from this note later.",
|
|
2720
2807
|
"Ask what entities it should stay attached to.",
|
|
2721
2808
|
"Ask whether it should be durable or temporary.",
|
|
2722
2809
|
"Ask about tags or author only if they help retrieval or handoff."
|
|
@@ -2797,6 +2884,17 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2797
2884
|
"Start the run instead of turning it into a longer intake."
|
|
2798
2885
|
]
|
|
2799
2886
|
},
|
|
2887
|
+
{
|
|
2888
|
+
focus: "work_adjustment",
|
|
2889
|
+
openingQuestion: "Which task or project should this time correction belong to?",
|
|
2890
|
+
coachingGoal: "Correct tracked minutes truthfully without pretending a live run happened.",
|
|
2891
|
+
askSequence: [
|
|
2892
|
+
"Ask what existing task or project the minutes belong to.",
|
|
2893
|
+
"Ask whether time should be added or removed.",
|
|
2894
|
+
"Ask what real work or correction the adjustment is meant to capture.",
|
|
2895
|
+
"Ask for a short audit note only if the reason would otherwise be unclear later."
|
|
2896
|
+
]
|
|
2897
|
+
},
|
|
2800
2898
|
{
|
|
2801
2899
|
focus: "self_observation",
|
|
2802
2900
|
openingQuestion: "What did you notice most clearly in that moment?",
|
|
@@ -2804,6 +2902,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2804
2902
|
askSequence: [
|
|
2805
2903
|
"Ask what was observed.",
|
|
2806
2904
|
"Reflect the moment without pretending it is already a finished interpretation.",
|
|
2905
|
+
"Ask for the smallest concrete slice if the observation still feels vague or global.",
|
|
2807
2906
|
"Ask when it happened or became noticeable unless timing is already clear.",
|
|
2808
2907
|
"Ask what it may connect to: pattern, belief, value, mode, task, project, or note.",
|
|
2809
2908
|
"Ask for tags or extra context only if that will help later review."
|
|
@@ -2849,6 +2948,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2849
2948
|
askSequence: [
|
|
2850
2949
|
"Ask what makes this item worth including in the catalog.",
|
|
2851
2950
|
"Ask what catalog or domain it belongs to if that is still unclear.",
|
|
2951
|
+
"Ask what would make the comparison confusing or unfair if the label stayed as-is.",
|
|
2852
2952
|
"Ask for a short clarifying description only if the label would be ambiguous later.",
|
|
2853
2953
|
"Ask about aliases or tags only if they help retrieval."
|
|
2854
2954
|
]
|
|
@@ -2876,6 +2976,28 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2876
2976
|
"Ask what makes the item distinct enough to compare usefully only if it is still a comparison candidate."
|
|
2877
2977
|
]
|
|
2878
2978
|
},
|
|
2979
|
+
{
|
|
2980
|
+
focus: "preference_judgment",
|
|
2981
|
+
openingQuestion: "What comparison are you actually trying to settle here?",
|
|
2982
|
+
coachingGoal: "Capture one pairwise preference decision with the right context instead of only logging a left-versus-right click.",
|
|
2983
|
+
askSequence: [
|
|
2984
|
+
"Ask what comparison the user is actually trying to settle.",
|
|
2985
|
+
"Ask which context or domain this judgment belongs to.",
|
|
2986
|
+
"Ask whether the result is left, right, tie, or skip.",
|
|
2987
|
+
"Ask for reason tags or strength only if they will improve later interpretation."
|
|
2988
|
+
]
|
|
2989
|
+
},
|
|
2990
|
+
{
|
|
2991
|
+
focus: "preference_signal",
|
|
2992
|
+
openingQuestion: "What do you want Forge to remember about this item right now?",
|
|
2993
|
+
coachingGoal: "Store a direct preference signal such as favorite, veto, bookmark, or compare-later with enough context to interpret it later.",
|
|
2994
|
+
askSequence: [
|
|
2995
|
+
"Ask what item the user wants to mark.",
|
|
2996
|
+
"Ask what signal they want to give it.",
|
|
2997
|
+
"Ask what domain or context this belongs to if that is still unclear.",
|
|
2998
|
+
"Ask about strength only if the user is expressing a gradient rather than a simple mark."
|
|
2999
|
+
]
|
|
3000
|
+
},
|
|
2879
3001
|
{
|
|
2880
3002
|
focus: "questionnaire_instrument",
|
|
2881
3003
|
openingQuestion: "What would this questionnaire help someone notice or track?",
|
|
@@ -2895,17 +3017,58 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2895
3017
|
askSequence: [
|
|
2896
3018
|
"Ask which questionnaire run this is about.",
|
|
2897
3019
|
"Ask whether the user wants to start, continue, review, or complete it.",
|
|
3020
|
+
"If the user wants to continue or finish, ask what state they are in right now before asking for more content.",
|
|
2898
3021
|
"If answering is still in progress, ask only for the next answer or note that matters."
|
|
2899
3022
|
]
|
|
2900
3023
|
},
|
|
3024
|
+
{
|
|
3025
|
+
focus: "movement",
|
|
3026
|
+
openingQuestion: "Are you trying to understand where you stayed and traveled, change one stay or trip, or answer a question about your movement behavior?",
|
|
3027
|
+
coachingGoal: "Clarify whether the user wants a time-in-place query, travel-history review, a missing-gap overlay, one stay or trip change, one place summary, or a link before choosing the dedicated movement route.",
|
|
3028
|
+
askSequence: [
|
|
3029
|
+
"Ask whether the user is trying to query behavior, add something manually, update an existing movement item, or link movement to another Forge entity.",
|
|
3030
|
+
"Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.",
|
|
3031
|
+
"Ask for the time window, place, or movement item that makes the question concrete.",
|
|
3032
|
+
"Ask what they are trying to notice, preserve, or answer through that movement context.",
|
|
3033
|
+
"Skip the meta lane question when the user already named the exact correction or review target and only one ambiguity remains.",
|
|
3034
|
+
"If the request is filling a missing-data gap, use a user-defined movement box rather than a raw stay or trip patch.",
|
|
3035
|
+
"When the user already gave a concrete correction like 'I stayed home during that missing block', confirm only the interval or place if needed, then create the overlay and read the timeline back."
|
|
3036
|
+
]
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
focus: "life_force",
|
|
3040
|
+
openingQuestion: "Do you want to understand the current energy picture, change how Forge models it, or log how you feel right now?",
|
|
3041
|
+
coachingGoal: "Clarify whether the job is overview, profile change, weekday-template editing, or a real-time fatigue signal before choosing the dedicated life-force route.",
|
|
3042
|
+
askSequence: [
|
|
3043
|
+
"Ask whether the job is overview, profile change, weekday-template change, or fatigue signaling.",
|
|
3044
|
+
"Ask what part of the current energy picture feels most important or inaccurate.",
|
|
3045
|
+
"Ask what should stay true if they are changing profile or template assumptions.",
|
|
3046
|
+
"If the user already named the life-force lane clearly, skip the meta lane question and ask only for the specific weekday, profile field, or signal that still matters.",
|
|
3047
|
+
"Route to the dedicated life-force path once the lane is clear."
|
|
3048
|
+
]
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
focus: "workbench",
|
|
3052
|
+
openingQuestion: "Are you trying to inspect a flow, change it, run it, or inspect one run's outputs?",
|
|
3053
|
+
coachingGoal: "Clarify whether the user wants flow discovery, editing, execution, run history, published outputs, or node-level inspection before using the dedicated workbench route family.",
|
|
3054
|
+
askSequence: [
|
|
3055
|
+
"Ask whether the job is flow discovery, one flow edit, execution, run history, published output, node-level inspection, or latest-node-output lookup.",
|
|
3056
|
+
"Ask which flow, slug, run, or node the request is about.",
|
|
3057
|
+
"Ask what the user is trying to learn, repair, or publish through that flow.",
|
|
3058
|
+
"If the user already named the flow and action clearly, skip the meta lane question and ask only for the missing run, node, or output scope.",
|
|
3059
|
+
"Route to the dedicated workbench route family once the execution lane is clear."
|
|
3060
|
+
]
|
|
3061
|
+
},
|
|
2901
3062
|
{
|
|
2902
3063
|
focus: "event_type",
|
|
2903
3064
|
openingQuestion: "What kind of moment keeps happening that you want future reports to name the same way each time?",
|
|
2904
3065
|
coachingGoal: "Create a reusable incident category that will actually help future reports stay consistent.",
|
|
2905
3066
|
askSequence: [
|
|
2906
3067
|
"Ask what kind of moment or incident this label should capture in lived terms.",
|
|
3068
|
+
"Reflect the repeated moment back in plain language before narrowing the wording.",
|
|
2907
3069
|
"Ask how narrow or broad it should be.",
|
|
2908
3070
|
"Ask what would count as inside versus outside the category if that boundary is still fuzzy.",
|
|
3071
|
+
"Offer a concise label if the lived meaning is clearer than the wording.",
|
|
2909
3072
|
"Ask for a short description only if the label could be ambiguous later."
|
|
2910
3073
|
]
|
|
2911
3074
|
},
|
|
@@ -2915,7 +3078,9 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
2915
3078
|
coachingGoal: "Create a reusable emotion label with enough clarity to use consistently later.",
|
|
2916
3079
|
askSequence: [
|
|
2917
3080
|
"Ask what this feeling is like in lived terms when the user says it.",
|
|
3081
|
+
"Reflect the felt signature back in plain language before you settle the label.",
|
|
2918
3082
|
"Ask what distinguishes it from nearby emotions if that matters.",
|
|
3083
|
+
"Offer a concise label if the felt meaning is clearer than the wording.",
|
|
2919
3084
|
"Ask for a broader category only if it will help later browsing or reporting."
|
|
2920
3085
|
]
|
|
2921
3086
|
}
|
|
@@ -2952,6 +3117,7 @@ const AGENT_ONBOARDING_PSYCHE_PLAYBOOKS = [
|
|
|
2952
3117
|
notes: [
|
|
2953
3118
|
"Use an ACT-style values clarification stance: values are directions to live toward, not boxes to complete.",
|
|
2954
3119
|
"Ask one or two questions at a time, reflect back the user's language, and only then move toward naming committed actions or linked work items.",
|
|
3120
|
+
"Reflect the pain, longing, or importance that makes the value alive before narrowing to action.",
|
|
2955
3121
|
"If the user says they want to understand it first, start with one orienting question before offering a formulation or save suggestion."
|
|
2956
3122
|
]
|
|
2957
3123
|
},
|
|
@@ -3036,7 +3202,8 @@ const AGENT_ONBOARDING_PSYCHE_PLAYBOOKS = [
|
|
|
3036
3202
|
"Keep the user close to observable behavior rather than jumping straight to labels.",
|
|
3037
3203
|
"When the behavior clearly belongs inside a larger loop, suggest linking or also mapping the related behavior_pattern.",
|
|
3038
3204
|
"If the user asks for understanding before storage, ask about the recent example and function of the move before classifying it.",
|
|
3039
|
-
"Ask what the move is trying to do for the user before moving into replacement planning."
|
|
3205
|
+
"Ask what the move is trying to do for the user before moving into replacement planning.",
|
|
3206
|
+
"Name the immediate protective job before discussing costs or alternatives."
|
|
3040
3207
|
]
|
|
3041
3208
|
},
|
|
3042
3209
|
{
|
|
@@ -3460,17 +3627,18 @@ const AGENT_ONBOARDING_TOOL_INPUT_CATALOG = [
|
|
|
3460
3627
|
},
|
|
3461
3628
|
{
|
|
3462
3629
|
toolName: "forge_connect_calendar_provider",
|
|
3463
|
-
summary: "Create a Forge calendar connection for Google, Apple, Exchange Online, or custom CalDAV.",
|
|
3630
|
+
summary: "Create a Forge calendar connection for Google, Apple, Exchange Online, calendars already configured on this Mac, or custom CalDAV.",
|
|
3464
3631
|
whenToUse: "Use only when the operator explicitly wants Forge connected to an external calendar provider.",
|
|
3465
|
-
inputShape: '{ provider: "google"|"apple"|"caldav"|"microsoft", label: string, username?: string, password?: string, serverUrl?: string, authSessionId?: string, selectedCalendarUrls: string[], forgeCalendarUrl?: string, createForgeCalendar?: boolean }',
|
|
3632
|
+
inputShape: '{ provider: "google"|"apple"|"caldav"|"microsoft"|"macos_local", label: string, username?: string, password?: string, serverUrl?: string, authSessionId?: string, sourceId?: string, selectedCalendarUrls: string[], forgeCalendarUrl?: string, createForgeCalendar?: boolean, replaceConnectionIds?: string[] }',
|
|
3466
3633
|
requiredFields: ["provider", "label", "provider-specific credentials"],
|
|
3467
3634
|
notes: [
|
|
3468
3635
|
"Google now uses an interactive localhost Authorization Code + PKCE flow. The user signs in interactively on the same machine running Forge, Forge exchanges the authorization code on the backend, and forge_connect_calendar_provider should only be used after a completed Google authSessionId exists.",
|
|
3469
3636
|
"Apple starts from https://caldav.icloud.com and autodiscovers the principal plus calendars after authentication.",
|
|
3470
3637
|
"Exchange Online uses Microsoft Graph. In the current Forge implementation it is read-only: Forge mirrors the selected calendars but does not publish work blocks or timeboxes back to Microsoft.",
|
|
3471
3638
|
"In the current self-hosted local runtime, Exchange Online now uses an interactive Microsoft public-client sign-in flow with PKCE after the operator has saved the Microsoft client ID, tenant, and redirect URI in Settings -> Calendar. Non-interactive callers should treat Microsoft connection setup as a Settings-owned operator action unless a completed authSessionId already exists.",
|
|
3639
|
+
"macos_local uses EventKit to read and write the calendars already configured on the host Mac. Discovery is grouped by host calendar source, and Forge replaces overlapping remote connections for the same account instead of keeping duplicate copies.",
|
|
3472
3640
|
"Custom CalDAV uses an account-level server URL, not a single calendar collection URL.",
|
|
3473
|
-
"Writable providers publish Forge work blocks and timeboxes
|
|
3641
|
+
"Writable providers publish Forge work blocks and timeboxes through one shared Forge write target. A new connection only needs its own write calendar when the runtime does not already have one."
|
|
3474
3642
|
],
|
|
3475
3643
|
example: '{"provider":"apple","label":"Primary Apple","username":"operator@example.com","password":"app-password","selectedCalendarUrls":["https://caldav.icloud.com/.../Family/"],"forgeCalendarUrl":"https://caldav.icloud.com/.../Forge/","createForgeCalendar":false}'
|
|
3476
3644
|
},
|
|
@@ -3516,7 +3684,7 @@ const AGENT_ONBOARDING_TOOL_INPUT_CATALOG = [
|
|
|
3516
3684
|
inputShape: '{ taskId: string, projectId?: string|null, title: string, startsAt: string, endsAt: string, source?: "manual"|"suggested"|"live_run" }',
|
|
3517
3685
|
requiredFields: ["taskId", "title", "startsAt", "endsAt"],
|
|
3518
3686
|
notes: [
|
|
3519
|
-
"Forge publishes these
|
|
3687
|
+
"Forge publishes these through the shared Forge write target during provider sync.",
|
|
3520
3688
|
"Live task runs can later attach to matching timeboxes.",
|
|
3521
3689
|
"This is a convenience helper; agents can also create task_timebox through forge_create_entities."
|
|
3522
3690
|
],
|
|
@@ -3698,6 +3866,10 @@ function buildAgentOnboardingPayload(request) {
|
|
|
3698
3866
|
calendar: "A connected calendar source mirrored into Forge. Calendar state combines provider events, recurring work blocks, and task timeboxes.",
|
|
3699
3867
|
workBlock: "A recurring half-day or custom time window such as Main Activity, Secondary Activity, Third Activity, Rest, Holiday, or Custom. Work blocks can allow or block work by default, can define active date bounds, and remain editable through the calendar surface.",
|
|
3700
3868
|
taskTimebox: "A planned or live calendar slot tied to a task. Timeboxes can be suggested in advance or created automatically from active task runs.",
|
|
3869
|
+
workAdjustment: "A work adjustment is a truthful signed minute correction on an existing task or project when real work happened but no live run was active.",
|
|
3870
|
+
movement: "Forge Movement is the first-class mobility surface. It is a timeline of stays and trips: stays capture time spent in the same place, and trips capture travel between places. Use it for time-in-place questions, travel-history review, specific stay or trip edits, selected-span aggregates, known places, and links to other Forge records rather than pretending stays and trips are normal batch CRUD entities.",
|
|
3871
|
+
lifeForce: "Life Force is Forge's energy-budget and fatigue model. Read it through the dedicated life-force payload and update it through focused profile, weekday-template, and fatigue-signal routes rather than generic entity CRUD.",
|
|
3872
|
+
workbench: "Workbench is Forge's graph-flow execution system. Treat flows, runs, published outputs, node results, and latest-node-output reads as a dedicated API family instead of a normal entity-batch surface.",
|
|
3701
3873
|
psyche: "Forge Psyche is the reflective domain for values, patterns, behaviors, beliefs, modes, and trigger reports. It is sensitive and should be handled deliberately."
|
|
3702
3874
|
},
|
|
3703
3875
|
psycheSubmoduleModel: {
|
|
@@ -3825,6 +3997,82 @@ function buildAgentOnboardingPayload(request) {
|
|
|
3825
3997
|
writeModel: "Create or update an observed note with frontmatter.observedAt. Manual reflections usually carry the Self-observation tag, while movement sync can also publish rolling observed notes tagged movement."
|
|
3826
3998
|
}
|
|
3827
3999
|
},
|
|
4000
|
+
specializedDomainSurfaces: {
|
|
4001
|
+
movement: {
|
|
4002
|
+
summary: "Dedicated movement workspace API. Use these routes for stays, trips, time-in-place questions, visited places, trip detail, selected-span aggregates, and user-defined overlays.",
|
|
4003
|
+
readRoutes: {
|
|
4004
|
+
day: "/api/v1/movement/day",
|
|
4005
|
+
month: "/api/v1/movement/month",
|
|
4006
|
+
allTime: "/api/v1/movement/all-time",
|
|
4007
|
+
timeline: "/api/v1/movement/timeline",
|
|
4008
|
+
places: "/api/v1/movement/places",
|
|
4009
|
+
tripDetail: "/api/v1/movement/trips/:id",
|
|
4010
|
+
selection: "/api/v1/movement/selection",
|
|
4011
|
+
settings: "/api/v1/movement/settings"
|
|
4012
|
+
},
|
|
4013
|
+
writeRoutes: {
|
|
4014
|
+
placeCreate: "/api/v1/movement/places",
|
|
4015
|
+
placeUpdate: "/api/v1/movement/places/:id",
|
|
4016
|
+
userBoxCreate: "/api/v1/movement/user-boxes",
|
|
4017
|
+
userBoxPreflight: "/api/v1/movement/user-boxes/preflight",
|
|
4018
|
+
userBoxUpdate: "/api/v1/movement/user-boxes/:id",
|
|
4019
|
+
automaticBoxInvalidate: "/api/v1/movement/automatic-boxes/:id/invalidate",
|
|
4020
|
+
stayUpdate: "/api/v1/movement/stays/:id",
|
|
4021
|
+
tripUpdate: "/api/v1/movement/trips/:id",
|
|
4022
|
+
tripPointUpdate: "/api/v1/movement/trips/:id/points/:pointId"
|
|
4023
|
+
},
|
|
4024
|
+
notes: [
|
|
4025
|
+
"Movement is not a normal batch CRUD entity family. It is a dedicated record of stays and trips: a stay means the user remained in the same place for a span of time, and a trip means they traveled between places.",
|
|
4026
|
+
"Use /api/v1/movement/day, /month, /all-time, /timeline, or /selection when the user wants behavioral answers such as how long they stayed at home, when they traveled, which places dominated a period, or what happened across a selected span.",
|
|
4027
|
+
"Use the movement write routes when the user wants to add a place or manual overlay, update a specific stay or trip, or attach movement context to another Forge record. If the user is filling a missing-data gap, the usual write path is a user-defined overlay box rather than a raw stay or trip patch.",
|
|
4028
|
+
"For an explicit statement like 'that missing block was me staying home', do not reopen broad intake. Preflight only if timing overlap is unclear, then create a user-defined `stay` box for that interval and read the updated timeline back."
|
|
4029
|
+
]
|
|
4030
|
+
},
|
|
4031
|
+
lifeForce: {
|
|
4032
|
+
summary: "Dedicated life-force API. Use it to read the current energy budget, drains, recommendations, and warnings, then patch only the parts that are meant to be user-controlled.",
|
|
4033
|
+
readRoutes: {
|
|
4034
|
+
overview: "/api/v1/life-force"
|
|
4035
|
+
},
|
|
4036
|
+
writeRoutes: {
|
|
4037
|
+
profile: "/api/v1/life-force/profile",
|
|
4038
|
+
weekdayTemplate: "/api/v1/life-force/templates/:weekday",
|
|
4039
|
+
fatigueSignal: "/api/v1/life-force/fatigue-signals"
|
|
4040
|
+
},
|
|
4041
|
+
notes: [
|
|
4042
|
+
"Life Force is a focused domain surface, not a batch CRUD entity type.",
|
|
4043
|
+
"Use GET /api/v1/life-force for the current overview payload with stats, drains, recommendations, and current-curve state.",
|
|
4044
|
+
"Patch the profile only for durable personal settings, update weekday templates only for the curve itself, and post fatigue signals for real-time tired or recovered observations."
|
|
4045
|
+
]
|
|
4046
|
+
},
|
|
4047
|
+
workbench: {
|
|
4048
|
+
summary: "Dedicated graph-flow API. Use it for flow catalog reads, flow CRUD, execution, run history, published outputs, node results, and latest successful node outputs.",
|
|
4049
|
+
readRoutes: {
|
|
4050
|
+
listFlows: "/api/v1/workbench/flows",
|
|
4051
|
+
flowById: "/api/v1/workbench/flows/:id",
|
|
4052
|
+
flowBySlug: "/api/v1/workbench/flows/by-slug/:slug",
|
|
4053
|
+
publishedOutput: "/api/v1/workbench/flows/:id/output",
|
|
4054
|
+
runs: "/api/v1/workbench/flows/:id/runs",
|
|
4055
|
+
runDetail: "/api/v1/workbench/flows/:id/runs/:runId",
|
|
4056
|
+
runNodes: "/api/v1/workbench/flows/:id/runs/:runId/nodes",
|
|
4057
|
+
nodeResult: "/api/v1/workbench/flows/:id/runs/:runId/nodes/:nodeId",
|
|
4058
|
+
latestNodeOutput: "/api/v1/workbench/flows/:id/nodes/:nodeId/output",
|
|
4059
|
+
boxCatalog: "/api/v1/workbench/catalog/boxes"
|
|
4060
|
+
},
|
|
4061
|
+
writeRoutes: {
|
|
4062
|
+
createFlow: "/api/v1/workbench/flows",
|
|
4063
|
+
updateFlow: "/api/v1/workbench/flows/:id",
|
|
4064
|
+
deleteFlow: "/api/v1/workbench/flows/:id",
|
|
4065
|
+
runFlow: "/api/v1/workbench/flows/:id/run",
|
|
4066
|
+
runByPayload: "/api/v1/workbench/run",
|
|
4067
|
+
chatFlow: "/api/v1/workbench/flows/:id/chat"
|
|
4068
|
+
},
|
|
4069
|
+
notes: [
|
|
4070
|
+
"Workbench is a dedicated execution surface, not a batch CRUD entity family.",
|
|
4071
|
+
"Use the flow routes when the agent needs stable public input contracts, published outputs, node-level results, or reusable execution history.",
|
|
4072
|
+
"Prefer the dedicated output and node-result routes over reverse-engineering raw traces."
|
|
4073
|
+
]
|
|
4074
|
+
}
|
|
4075
|
+
},
|
|
3828
4076
|
readModelOnlySurfaces: {
|
|
3829
4077
|
sleepOverview: "/api/v1/health/sleep",
|
|
3830
4078
|
sportsOverview: "/api/v1/health/fitness",
|
|
@@ -3908,6 +4156,24 @@ function buildAgentOnboardingPayload(request) {
|
|
|
3908
4156
|
weeklyReview: "/api/v1/reviews/weekly",
|
|
3909
4157
|
sleepOverview: "/api/v1/health/sleep",
|
|
3910
4158
|
sportsOverview: "/api/v1/health/fitness",
|
|
4159
|
+
lifeForce: "/api/v1/life-force",
|
|
4160
|
+
lifeForceProfile: "/api/v1/life-force/profile",
|
|
4161
|
+
lifeForceWeekdayTemplate: "/api/v1/life-force/templates/:weekday",
|
|
4162
|
+
lifeForceFatigueSignals: "/api/v1/life-force/fatigue-signals",
|
|
4163
|
+
movementDay: "/api/v1/movement/day",
|
|
4164
|
+
movementMonth: "/api/v1/movement/month",
|
|
4165
|
+
movementTimeline: "/api/v1/movement/timeline",
|
|
4166
|
+
movementAllTime: "/api/v1/movement/all-time",
|
|
4167
|
+
movementPlaces: "/api/v1/movement/places",
|
|
4168
|
+
movementTripDetail: "/api/v1/movement/trips/:id",
|
|
4169
|
+
movementSelection: "/api/v1/movement/selection",
|
|
4170
|
+
movementUserBoxPreflight: "/api/v1/movement/user-boxes/preflight",
|
|
4171
|
+
workbenchFlows: "/api/v1/workbench/flows",
|
|
4172
|
+
workbenchFlowBySlug: "/api/v1/workbench/flows/by-slug/:slug",
|
|
4173
|
+
workbenchPublishedOutput: "/api/v1/workbench/flows/:id/output",
|
|
4174
|
+
workbenchRunDetail: "/api/v1/workbench/flows/:id/runs/:runId",
|
|
4175
|
+
workbenchNodeResult: "/api/v1/workbench/flows/:id/runs/:runId/nodes/:nodeId",
|
|
4176
|
+
workbenchLatestNodeOutput: "/api/v1/workbench/flows/:id/nodes/:nodeId/output",
|
|
3911
4177
|
wikiSettings: "/api/v1/wiki/settings",
|
|
3912
4178
|
wikiSearch: "/api/v1/wiki/search",
|
|
3913
4179
|
wikiHealth: "/api/v1/wiki/health",
|
|
@@ -7028,6 +7294,36 @@ export async function buildServer(options = {}) {
|
|
|
7028
7294
|
providers: listCalendarProviderMetadata(),
|
|
7029
7295
|
connections: listConnectedCalendarConnections()
|
|
7030
7296
|
}));
|
|
7297
|
+
app.get("/api/v1/calendar/macos-local/status", async (request) => {
|
|
7298
|
+
requireScopedAccess(request.headers, ["write"], {
|
|
7299
|
+
route: "/api/v1/calendar/macos-local/status"
|
|
7300
|
+
});
|
|
7301
|
+
return await getMacOSLocalCalendarAccessStatus();
|
|
7302
|
+
});
|
|
7303
|
+
app.post("/api/v1/calendar/macos-local/request-access", async (request) => {
|
|
7304
|
+
requireScopedAccess(request.headers, ["write"], {
|
|
7305
|
+
route: "/api/v1/calendar/macos-local/request-access"
|
|
7306
|
+
});
|
|
7307
|
+
return await requestMacOSLocalCalendarAccess();
|
|
7308
|
+
});
|
|
7309
|
+
app.get("/api/v1/calendar/macos-local/discovery", async (request) => {
|
|
7310
|
+
const auth = requireScopedAccess(request.headers, ["write"], { route: "/api/v1/calendar/macos-local/discovery" });
|
|
7311
|
+
const discovery = await discoverMacOSLocalCalendarSources();
|
|
7312
|
+
recordActivityEvent({
|
|
7313
|
+
entityType: "calendar_connection",
|
|
7314
|
+
entityId: "calendar_discovery_macos_local",
|
|
7315
|
+
eventType: "calendar_connection_discovered",
|
|
7316
|
+
title: "Calendar discovery completed for macOS local calendars",
|
|
7317
|
+
description: "Forge discovered the calendars already configured on this Mac before connection setup.",
|
|
7318
|
+
actor: auth.actor ?? null,
|
|
7319
|
+
source: auth.source,
|
|
7320
|
+
metadata: {
|
|
7321
|
+
provider: "macos_local",
|
|
7322
|
+
sources: discovery.sources.length
|
|
7323
|
+
}
|
|
7324
|
+
});
|
|
7325
|
+
return { discovery };
|
|
7326
|
+
});
|
|
7031
7327
|
app.post("/api/v1/calendar/oauth/google/start", async (request) => {
|
|
7032
7328
|
requireScopedAccess(request.headers, ["write"], {
|
|
7033
7329
|
route: "/api/v1/calendar/oauth/google/start"
|
|
@@ -7862,6 +8158,14 @@ export async function buildServer(options = {}) {
|
|
|
7862
8158
|
existingConnectionId: error.connectionId
|
|
7863
8159
|
};
|
|
7864
8160
|
}
|
|
8161
|
+
if (error instanceof CalendarConnectionOverlapError) {
|
|
8162
|
+
reply.code(409);
|
|
8163
|
+
return {
|
|
8164
|
+
code: "calendar_connection_overlap",
|
|
8165
|
+
error: error.message,
|
|
8166
|
+
overlappingConnectionIds: error.connectionIds
|
|
8167
|
+
};
|
|
8168
|
+
}
|
|
7865
8169
|
throw error;
|
|
7866
8170
|
}
|
|
7867
8171
|
});
|
|
@@ -7896,14 +8200,27 @@ export async function buildServer(options = {}) {
|
|
|
7896
8200
|
app.post("/api/v1/calendar/connections/:id/sync", async (request, reply) => {
|
|
7897
8201
|
const auth = requireScopedAccess(request.headers, ["write"], { route: "/api/v1/calendar/connections/:id/sync" });
|
|
7898
8202
|
const { id } = request.params;
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
8203
|
+
try {
|
|
8204
|
+
const connection = await syncCalendarConnection(id, managers.secrets, toActivityContext(auth));
|
|
8205
|
+
if (!connection) {
|
|
8206
|
+
reply.code(404);
|
|
8207
|
+
return { error: "Calendar connection not found" };
|
|
8208
|
+
}
|
|
8209
|
+
return {
|
|
8210
|
+
connection: listConnectedCalendarConnections().find((entry) => entry.id === id)
|
|
8211
|
+
};
|
|
8212
|
+
}
|
|
8213
|
+
catch (error) {
|
|
8214
|
+
if (error instanceof Error &&
|
|
8215
|
+
error.message.includes("replaced by a newer canonical connection")) {
|
|
8216
|
+
reply.code(409);
|
|
8217
|
+
return {
|
|
8218
|
+
code: "calendar_connection_superseded",
|
|
8219
|
+
error: error.message
|
|
8220
|
+
};
|
|
8221
|
+
}
|
|
8222
|
+
throw error;
|
|
7903
8223
|
}
|
|
7904
|
-
return {
|
|
7905
|
-
connection: listConnectedCalendarConnections().find((entry) => entry.id === id)
|
|
7906
|
-
};
|
|
7907
8224
|
});
|
|
7908
8225
|
app.delete("/api/v1/calendar/connections/:id", async (request, reply) => {
|
|
7909
8226
|
const auth = requireScopedAccess(request.headers, ["write"], { route: "/api/v1/calendar/connections/:id" });
|