forge-openclaw-plugin 0.2.44 → 0.2.47
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/dist/assets/index-BejDHw1R.js +91 -0
- package/dist/assets/index-BejDHw1R.js.map +1 -0
- package/dist/index.html +1 -1
- package/dist/openclaw/api-client.d.ts +1 -0
- package/dist/openclaw/plugin-entry-shared.js +13 -0
- package/dist/openclaw/session-bootstrap.d.ts +11 -0
- package/dist/openclaw/session-bootstrap.js +151 -13
- package/dist/server/server/migrations/050_agent_token_bootstrap_policy.sql +2 -0
- package/dist/server/server/migrations/051_agent_token_scope_policy.sql +2 -0
- package/dist/server/server/src/app.js +193 -43
- package/dist/server/server/src/managers/platform/authentication-manager.js +5 -0
- package/dist/server/server/src/managers/platform/session-manager.js +5 -0
- package/dist/server/server/src/openapi.js +59 -0
- package/dist/server/server/src/repositories/settings.js +26 -6
- package/dist/server/server/src/types.js +83 -1
- package/dist/server/src/lib/schemas.js +16 -1
- package/openclaw.plugin.json +10 -1
- package/package.json +1 -1
- package/server/migrations/050_agent_token_bootstrap_policy.sql +2 -0
- package/server/migrations/051_agent_token_scope_policy.sql +2 -0
- package/dist/assets/index-s24CefIb.js +0 -91
- package/dist/assets/index-s24CefIb.js.map +0 -1
|
@@ -56,7 +56,7 @@ import { PSYCHE_ENTITY_TYPES, createBehaviorSchema, createBeliefEntrySchema, cre
|
|
|
56
56
|
import { createQuestionnaireInstrumentSchema, publishQuestionnaireVersionSchema, startQuestionnaireRunSchema, updateQuestionnaireRunSchema, updateQuestionnaireVersionSchema } from "./questionnaire-types.js";
|
|
57
57
|
import { createPreferenceCatalogItemSchema, createPreferenceCatalogSchema, createPreferenceContextSchema, createPreferenceItemSchema, enqueueEntityPreferenceItemSchema, mergePreferenceContextsSchema, preferenceWorkspaceQuerySchema, startPreferenceGameSchema, submitAbsoluteSignalSchema, submitPairwiseJudgmentSchema, updatePreferenceCatalogItemSchema, updatePreferenceCatalogSchema, updatePreferenceContextSchema, updatePreferenceItemSchema, updatePreferenceScoreSchema } from "./preferences-types.js";
|
|
58
58
|
import { createDataBackupSchema, dataExportQuerySchema, restoreDataBackupSchema, switchDataRootSchema, updateDataManagementSettingsSchema } from "./data-management-types.js";
|
|
59
|
-
import { activityListQuerySchema, activitySourceSchema, createAgentActionSchema, createAgentRuntimeSessionEventSchema, createAgentRuntimeSessionSchema, createAgentTokenSchema, createAiConnectorSchema, createAiProcessorLinkSchema, createAiProcessorSchema, runAiConnectorSchema, writeSurfaceLayoutSchema, upsertAiModelConnectionSchema, testAiModelConnectionSchema, submitOpenAiCodexOauthManualCodeSchema, batchCreateEntitiesSchema, batchDeleteEntitiesSchema, batchRestoreEntitiesSchema, batchSearchEntitiesSchema, batchUpdateEntitiesSchema, createGoalSchema, createInsightFeedbackSchema, createInsightSchema, createStrategySchema, createUserSchema, createNoteSchema, createProjectSchema, createManualRewardGrantSchema, createCalendarEventSchema, createHabitCheckInSchema, createCalendarConnectionSchema, createDiagnosticLogSchema, discoverCalendarConnectionSchema, startGoogleCalendarOauthSchema, startMicrosoftCalendarOauthSchema, testMicrosoftCalendarOauthConfigurationSchema, createHabitSchema, createTaskTimeboxSchema, createWorkBlockTemplateSchema, createSessionEventSchema, createWorkAdjustmentSchema, createTagSchema, calendarOverviewQuerySchema, psycheObservationCalendarExportQuerySchema, notesListQuerySchema, updateTagSchema, createTaskSchema, diagnosticLogListQuerySchema, disconnectAgentRuntimeSessionSchema, eventsListQuerySchema, heartbeatAgentRuntimeSessionSchema, operatorLogWorkSchema, projectBoardPayloadSchema, projectListQuerySchema, entityDeleteQuerySchema, removeActivityEventSchema, reconnectAgentRuntimeSessionSchema, resolveApprovalRequestSchema, rewardsLedgerQuerySchema, habitListQuerySchema, taskContextPayloadSchema, taskRunClaimSchema, taskRunFocusSchema, taskRunFinishSchema, taskRunHeartbeatSchema, taskRunListQuerySchema, taskSplitCreateSchema, taskListQuerySchema, tagSuggestionRequestSchema, uncompleteTaskSchema, updateSettingsSchema, updateGoalSchema, updateHabitSchema, updateInsightSchema, updateStrategySchema, updateUserSchema, updateCalendarConnectionSchema, updateCalendarEventSchema, updateNoteSchema, updateProjectSchema, updateRewardRuleSchema, updateTaskTimeboxSchema, updateTaskSchema, lifeForceProfilePatchSchema, lifeForceTemplateUpdateSchema, fatigueSignalCreateSchema, updateUserAccessGrantSchema, updateWorkBlockTemplateSchema, updateAiConnectorSchema, updateAiProcessorSchema, runAiProcessorSchema, workAdjustmentResultSchema, finalizeWeeklyReviewResultSchema, goalListQuerySchema, recommendTaskTimeboxesSchema, strategyListQuerySchema } from "./types.js";
|
|
59
|
+
import { activityListQuerySchema, activitySourceSchema, defaultAgentBootstrapPolicy, defaultAgentScopePolicy, createAgentActionSchema, createAgentRuntimeSessionEventSchema, createAgentRuntimeSessionSchema, createAgentTokenSchema, createAiConnectorSchema, createAiProcessorLinkSchema, createAiProcessorSchema, runAiConnectorSchema, writeSurfaceLayoutSchema, upsertAiModelConnectionSchema, testAiModelConnectionSchema, submitOpenAiCodexOauthManualCodeSchema, batchCreateEntitiesSchema, batchDeleteEntitiesSchema, batchRestoreEntitiesSchema, batchSearchEntitiesSchema, batchUpdateEntitiesSchema, createGoalSchema, createInsightFeedbackSchema, createInsightSchema, createStrategySchema, createUserSchema, createNoteSchema, createProjectSchema, createManualRewardGrantSchema, createCalendarEventSchema, createHabitCheckInSchema, createCalendarConnectionSchema, createDiagnosticLogSchema, discoverCalendarConnectionSchema, startGoogleCalendarOauthSchema, startMicrosoftCalendarOauthSchema, testMicrosoftCalendarOauthConfigurationSchema, createHabitSchema, createTaskTimeboxSchema, createWorkBlockTemplateSchema, createSessionEventSchema, createWorkAdjustmentSchema, createTagSchema, calendarOverviewQuerySchema, psycheObservationCalendarExportQuerySchema, notesListQuerySchema, updateTagSchema, createTaskSchema, diagnosticLogListQuerySchema, disconnectAgentRuntimeSessionSchema, eventsListQuerySchema, heartbeatAgentRuntimeSessionSchema, operatorLogWorkSchema, projectBoardPayloadSchema, projectListQuerySchema, entityDeleteQuerySchema, removeActivityEventSchema, reconnectAgentRuntimeSessionSchema, resolveApprovalRequestSchema, rewardsLedgerQuerySchema, habitListQuerySchema, taskContextPayloadSchema, taskRunClaimSchema, taskRunFocusSchema, taskRunFinishSchema, taskRunHeartbeatSchema, taskRunListQuerySchema, taskSplitCreateSchema, taskListQuerySchema, tagSuggestionRequestSchema, uncompleteTaskSchema, updateSettingsSchema, updateGoalSchema, updateHabitSchema, updateInsightSchema, updateStrategySchema, updateUserSchema, updateCalendarConnectionSchema, updateCalendarEventSchema, updateNoteSchema, updateProjectSchema, updateRewardRuleSchema, updateTaskTimeboxSchema, updateTaskSchema, lifeForceProfilePatchSchema, lifeForceTemplateUpdateSchema, fatigueSignalCreateSchema, updateUserAccessGrantSchema, updateWorkBlockTemplateSchema, updateAiConnectorSchema, updateAiProcessorSchema, runAiProcessorSchema, workAdjustmentResultSchema, finalizeWeeklyReviewResultSchema, goalListQuerySchema, recommendTaskTimeboxesSchema, strategyListQuerySchema } from "./types.js";
|
|
60
60
|
import { buildOpenApiDocument } from "./openapi.js";
|
|
61
61
|
import { registerWebRoutes } from "./web.js";
|
|
62
62
|
import { createManagerRuntime } from "./managers/runtime.js";
|
|
@@ -3936,6 +3936,9 @@ function buildValidationHelp(method, routeUrl, issues) {
|
|
|
3936
3936
|
}
|
|
3937
3937
|
function buildAgentOnboardingPayload(request) {
|
|
3938
3938
|
const origin = getRequestOrigin(request);
|
|
3939
|
+
const auth = parseRequestAuth(request.headers);
|
|
3940
|
+
const effectiveBootstrapPolicy = auth.token?.bootstrapPolicy ?? defaultAgentBootstrapPolicy;
|
|
3941
|
+
const effectiveScopePolicy = auth.token?.scopePolicy ?? defaultAgentScopePolicy;
|
|
3939
3942
|
return {
|
|
3940
3943
|
forgeBaseUrl: origin,
|
|
3941
3944
|
webAppUrl: `${origin}/forge/`,
|
|
@@ -3962,6 +3965,10 @@ function buildAgentOnboardingPayload(request) {
|
|
|
3962
3965
|
recommendedTrustLevel: "trusted",
|
|
3963
3966
|
recommendedAutonomyMode: "approval_required",
|
|
3964
3967
|
recommendedApprovalMode: "approval_by_default",
|
|
3968
|
+
defaultBootstrapPolicy: defaultAgentBootstrapPolicy,
|
|
3969
|
+
effectiveBootstrapPolicy,
|
|
3970
|
+
defaultScopePolicy: defaultAgentScopePolicy,
|
|
3971
|
+
effectiveScopePolicy,
|
|
3965
3972
|
authModes: {
|
|
3966
3973
|
operatorSession: {
|
|
3967
3974
|
label: "Quick connect",
|
|
@@ -3971,7 +3978,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
3971
3978
|
},
|
|
3972
3979
|
managedToken: {
|
|
3973
3980
|
label: "Managed token",
|
|
3974
|
-
summary: "Use a long-lived token when you want explicit scoped auth, remote non-Tailscale access,
|
|
3981
|
+
summary: "Use a long-lived token when you want explicit scoped auth, remote non-Tailscale access, durable agent credentials, a custom bootstrap budget, or default user/project/tag read boundaries per agent.",
|
|
3975
3982
|
tokenRequired: true
|
|
3976
3983
|
}
|
|
3977
3984
|
},
|
|
@@ -4552,6 +4559,11 @@ function parseRequestAuth(headers) {
|
|
|
4552
4559
|
const source = token ? "agent" : activity.source;
|
|
4553
4560
|
return {
|
|
4554
4561
|
token,
|
|
4562
|
+
scope: {
|
|
4563
|
+
userIds: token?.scopePolicy.userIds ?? [],
|
|
4564
|
+
projectIds: token?.scopePolicy.projectIds ?? [],
|
|
4565
|
+
tagIds: token?.scopePolicy.tagIds ?? []
|
|
4566
|
+
},
|
|
4555
4567
|
actor,
|
|
4556
4568
|
source,
|
|
4557
4569
|
activity: {
|
|
@@ -4641,6 +4653,105 @@ function resolveScopedUserIds(query) {
|
|
|
4641
4653
|
const unique = Array.from(new Set(values));
|
|
4642
4654
|
return unique.length > 0 ? unique : undefined;
|
|
4643
4655
|
}
|
|
4656
|
+
const EMPTY_SCOPED_USER_IDS = ["__forge_scope_none__"];
|
|
4657
|
+
function normalizeScopedUserIdsForReads(options) {
|
|
4658
|
+
const validUserIdSet = new Set(options.validUserIds);
|
|
4659
|
+
const validScopedUserIds = options.scope.userIds !== undefined
|
|
4660
|
+
? options.scope.userIds.filter((userId) => validUserIdSet.has(userId))
|
|
4661
|
+
: undefined;
|
|
4662
|
+
const scopedUserIdsForReads = options.scope.enforceUserIds &&
|
|
4663
|
+
validScopedUserIds !== undefined &&
|
|
4664
|
+
validScopedUserIds.length === 0
|
|
4665
|
+
? EMPTY_SCOPED_USER_IDS
|
|
4666
|
+
: validScopedUserIds && validScopedUserIds.length > 0
|
|
4667
|
+
? validScopedUserIds
|
|
4668
|
+
: undefined;
|
|
4669
|
+
return {
|
|
4670
|
+
validScopedUserIds,
|
|
4671
|
+
scopedUserIdsForReads
|
|
4672
|
+
};
|
|
4673
|
+
}
|
|
4674
|
+
function resolveEffectiveReadScope(query, auth) {
|
|
4675
|
+
const requestedUserIds = resolveScopedUserIds(query);
|
|
4676
|
+
const tokenUserIds = auth.token?.scopePolicy.userIds ?? [];
|
|
4677
|
+
const effectiveUserIds = tokenUserIds.length > 0
|
|
4678
|
+
? requestedUserIds
|
|
4679
|
+
? requestedUserIds.filter((userId) => tokenUserIds.includes(userId))
|
|
4680
|
+
: [...tokenUserIds]
|
|
4681
|
+
: requestedUserIds;
|
|
4682
|
+
return {
|
|
4683
|
+
userIds: effectiveUserIds !== undefined ? Array.from(new Set(effectiveUserIds)) : undefined,
|
|
4684
|
+
enforceUserIds: tokenUserIds.length > 0,
|
|
4685
|
+
projectIds: auth.token?.scopePolicy.projectIds ?? [],
|
|
4686
|
+
tagIds: auth.token?.scopePolicy.tagIds ?? []
|
|
4687
|
+
};
|
|
4688
|
+
}
|
|
4689
|
+
function hasScopeFilters(scope) {
|
|
4690
|
+
return scope.projectIds.length > 0 || scope.tagIds.length > 0;
|
|
4691
|
+
}
|
|
4692
|
+
function intersects(values, allowed) {
|
|
4693
|
+
if (!values || values.length === 0) {
|
|
4694
|
+
return false;
|
|
4695
|
+
}
|
|
4696
|
+
return values.some((value) => allowed.includes(value));
|
|
4697
|
+
}
|
|
4698
|
+
function applyTaskScope(tasks, scope) {
|
|
4699
|
+
if (!hasScopeFilters(scope)) {
|
|
4700
|
+
return tasks;
|
|
4701
|
+
}
|
|
4702
|
+
return tasks.filter((task) => {
|
|
4703
|
+
if (scope.projectIds.length > 0 &&
|
|
4704
|
+
(!task.projectId || !scope.projectIds.includes(task.projectId))) {
|
|
4705
|
+
return false;
|
|
4706
|
+
}
|
|
4707
|
+
if (scope.tagIds.length > 0 && !intersects(task.tagIds, scope.tagIds)) {
|
|
4708
|
+
return false;
|
|
4709
|
+
}
|
|
4710
|
+
return true;
|
|
4711
|
+
});
|
|
4712
|
+
}
|
|
4713
|
+
function applyProjectScope(projects, scope) {
|
|
4714
|
+
if (!hasScopeFilters(scope)) {
|
|
4715
|
+
return projects;
|
|
4716
|
+
}
|
|
4717
|
+
return projects.filter((project) => {
|
|
4718
|
+
if (scope.projectIds.length > 0 &&
|
|
4719
|
+
!scope.projectIds.includes(project.id)) {
|
|
4720
|
+
return false;
|
|
4721
|
+
}
|
|
4722
|
+
if (scope.tagIds.length > 0 &&
|
|
4723
|
+
!intersects(project.tagIds ?? undefined, scope.tagIds)) {
|
|
4724
|
+
return false;
|
|
4725
|
+
}
|
|
4726
|
+
return true;
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4729
|
+
function applyGoalScope(goals, scope, allowedGoalIds) {
|
|
4730
|
+
if (!hasScopeFilters(scope)) {
|
|
4731
|
+
return goals;
|
|
4732
|
+
}
|
|
4733
|
+
return goals.filter((goal) => (scope.tagIds.length > 0 &&
|
|
4734
|
+
intersects(goal.tagIds ?? undefined, scope.tagIds)) ||
|
|
4735
|
+
allowedGoalIds.has(goal.id));
|
|
4736
|
+
}
|
|
4737
|
+
function applyHabitScope(habits, scope, allowedGoalIds, allowedTaskIds) {
|
|
4738
|
+
if (!hasScopeFilters(scope)) {
|
|
4739
|
+
return habits;
|
|
4740
|
+
}
|
|
4741
|
+
return habits.filter((habit) => intersects(habit.linkedProjectIds, scope.projectIds) ||
|
|
4742
|
+
intersects(habit.linkedTaskIds, [...allowedTaskIds]) ||
|
|
4743
|
+
intersects(habit.linkedGoalIds, [...allowedGoalIds]));
|
|
4744
|
+
}
|
|
4745
|
+
function applyStrategyScope(strategies, scope, allowedGoalIds) {
|
|
4746
|
+
if (!hasScopeFilters(scope)) {
|
|
4747
|
+
return strategies;
|
|
4748
|
+
}
|
|
4749
|
+
return strategies.filter((strategy) => intersects(strategy.targetProjectIds, scope.projectIds) ||
|
|
4750
|
+
intersects(strategy.targetGoalIds, [...allowedGoalIds]) ||
|
|
4751
|
+
strategy.linkedEntities.some((link) => (link.entityType === "project" &&
|
|
4752
|
+
scope.projectIds.includes(link.entityId)) ||
|
|
4753
|
+
(link.entityType === "goal" && allowedGoalIds.has(link.entityId))));
|
|
4754
|
+
}
|
|
4644
4755
|
function attachMovementTimelineSleepOverlays(movement, userIds) {
|
|
4645
4756
|
const rangeStart = movement.segments.reduce((earliest, segment) => {
|
|
4646
4757
|
if (!earliest || Date.parse(segment.startedAt) < Date.parse(earliest)) {
|
|
@@ -4687,18 +4798,26 @@ function syncEntityOwnerFromBody(options) {
|
|
|
4687
4798
|
const owner = resolveUserForMutation(requestedUserId, options.fallbackLabel);
|
|
4688
4799
|
setEntityOwner(options.entityType, options.entityId, owner.id);
|
|
4689
4800
|
}
|
|
4690
|
-
function buildV1Context(
|
|
4801
|
+
function buildV1Context(scope = {
|
|
4802
|
+
userIds: undefined,
|
|
4803
|
+
enforceUserIds: false,
|
|
4804
|
+
projectIds: [],
|
|
4805
|
+
tagIds: []
|
|
4806
|
+
}) {
|
|
4691
4807
|
const users = listUsers();
|
|
4692
|
-
const
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
const
|
|
4697
|
-
const tasks = filterOwnedEntities("task", listTasks(),
|
|
4698
|
-
const
|
|
4699
|
-
|
|
4700
|
-
const
|
|
4701
|
-
|
|
4808
|
+
const { validScopedUserIds, scopedUserIdsForReads } = normalizeScopedUserIdsForReads({
|
|
4809
|
+
scope,
|
|
4810
|
+
validUserIds: users.map((user) => user.id)
|
|
4811
|
+
});
|
|
4812
|
+
const projects = applyProjectScope(listProjectSummaries({ userIds: scopedUserIdsForReads }), scope);
|
|
4813
|
+
const tasks = applyTaskScope(filterOwnedEntities("task", listTasks(), scopedUserIdsForReads), scope);
|
|
4814
|
+
const goals = applyGoalScope(filterOwnedEntities("goal", listGoals(), scopedUserIdsForReads), scope, new Set([...projects.map((project) => project.goalId), ...tasks.map((task) => task.goalId)]
|
|
4815
|
+
.filter((value) => typeof value === "string" && value.length > 0)));
|
|
4816
|
+
const habits = applyHabitScope(filterOwnedEntities("habit", listHabits(), scopedUserIdsForReads), scope, new Set(goals.map((goal) => goal.id)), new Set(tasks.map((task) => task.id)));
|
|
4817
|
+
const strategies = applyStrategyScope(listStrategies({ userIds: scopedUserIdsForReads }), scope, new Set(goals.map((goal) => goal.id)));
|
|
4818
|
+
const dashboard = getDashboard({ userIds: scopedUserIdsForReads });
|
|
4819
|
+
const selectedUsers = validScopedUserIds !== undefined
|
|
4820
|
+
? users.filter((user) => validScopedUserIds.includes(user.id))
|
|
4702
4821
|
: users;
|
|
4703
4822
|
return {
|
|
4704
4823
|
meta: {
|
|
@@ -4710,29 +4829,33 @@ function buildV1Context(userIds) {
|
|
|
4710
4829
|
},
|
|
4711
4830
|
metrics: buildGamificationProfile(goals, tasks, habits),
|
|
4712
4831
|
dashboard,
|
|
4713
|
-
overview: getOverviewContext(new Date(), { userIds:
|
|
4714
|
-
today: getTodayContext(new Date(), { userIds:
|
|
4715
|
-
risk: getRiskContext(new Date(), { userIds:
|
|
4832
|
+
overview: getOverviewContext(new Date(), { userIds: scopedUserIdsForReads }),
|
|
4833
|
+
today: getTodayContext(new Date(), { userIds: scopedUserIdsForReads }),
|
|
4834
|
+
risk: getRiskContext(new Date(), { userIds: scopedUserIdsForReads }),
|
|
4716
4835
|
goals,
|
|
4717
|
-
projects
|
|
4836
|
+
projects,
|
|
4718
4837
|
tags: listTags(),
|
|
4719
4838
|
tasks,
|
|
4720
4839
|
habits,
|
|
4721
4840
|
users,
|
|
4722
|
-
strategies
|
|
4841
|
+
strategies,
|
|
4723
4842
|
userScope: {
|
|
4724
|
-
selectedUserIds:
|
|
4843
|
+
selectedUserIds: validScopedUserIds ?? [],
|
|
4725
4844
|
selectedUsers
|
|
4726
4845
|
},
|
|
4727
|
-
activeTaskRuns:
|
|
4846
|
+
activeTaskRuns: scope.enforceUserIds &&
|
|
4847
|
+
validScopedUserIds !== undefined &&
|
|
4848
|
+
validScopedUserIds.length === 0
|
|
4849
|
+
? []
|
|
4850
|
+
: listTaskRuns({ active: true, limit: 25, userIds: scopedUserIdsForReads }),
|
|
4728
4851
|
activity: dashboard.recentActivity,
|
|
4729
|
-
lifeForce: buildLifeForcePayload(new Date(),
|
|
4852
|
+
lifeForce: buildLifeForcePayload(new Date(), scopedUserIdsForReads)
|
|
4730
4853
|
};
|
|
4731
4854
|
}
|
|
4732
|
-
function buildXpMetricsPayload() {
|
|
4733
|
-
const goals = listGoals();
|
|
4734
|
-
const tasks = listTasks();
|
|
4735
|
-
const habits = listHabits();
|
|
4855
|
+
function buildXpMetricsPayload(input = {}) {
|
|
4856
|
+
const goals = input.goals ?? listGoals();
|
|
4857
|
+
const tasks = input.tasks ?? listTasks();
|
|
4858
|
+
const habits = input.habits ?? listHabits();
|
|
4736
4859
|
const rules = listRewardRules();
|
|
4737
4860
|
const gamificationOverview = buildGamificationOverview(goals, tasks, habits);
|
|
4738
4861
|
const dailyAmbientCap = rules
|
|
@@ -4791,13 +4914,26 @@ function describeWorkAdjustment(input) {
|
|
|
4791
4914
|
: `${appliedLabel} ${direction} from the tracked work total.`
|
|
4792
4915
|
};
|
|
4793
4916
|
}
|
|
4794
|
-
function buildOperatorContext(
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4917
|
+
function buildOperatorContext(scope = {
|
|
4918
|
+
userIds: undefined,
|
|
4919
|
+
enforceUserIds: false,
|
|
4920
|
+
projectIds: [],
|
|
4921
|
+
tagIds: []
|
|
4922
|
+
}) {
|
|
4923
|
+
const users = listUsers();
|
|
4924
|
+
const { scopedUserIdsForReads } = normalizeScopedUserIdsForReads({
|
|
4925
|
+
scope,
|
|
4926
|
+
validUserIds: users.map((user) => user.id)
|
|
4927
|
+
});
|
|
4928
|
+
const tasks = applyTaskScope(filterOwnedEntities("task", listTasks(), scopedUserIdsForReads), scope);
|
|
4929
|
+
const dueHabits = filterOwnedEntities("habit", listHabits({ dueToday: true }), scopedUserIdsForReads);
|
|
4930
|
+
const activeProjects = applyProjectScope(listProjectSummaries({
|
|
4798
4931
|
status: "active",
|
|
4799
|
-
userIds
|
|
4800
|
-
}).filter((project) => project.activeTaskCount > 0 || project.completedTaskCount > 0);
|
|
4932
|
+
userIds: scopedUserIdsForReads
|
|
4933
|
+
}), scope).filter((project) => project.activeTaskCount > 0 || project.completedTaskCount > 0);
|
|
4934
|
+
const goals = applyGoalScope(filterOwnedEntities("goal", listGoals(), scopedUserIdsForReads), scope, new Set([...activeProjects.map((project) => project.goalId), ...tasks.map((task) => task.goalId)]
|
|
4935
|
+
.filter((value) => typeof value === "string" && value.length > 0)));
|
|
4936
|
+
const scopedHabits = applyHabitScope(dueHabits, scope, new Set(goals.map((goal) => goal.id)), new Set(tasks.map((task) => task.id))).slice(0, 12);
|
|
4801
4937
|
const focusTasks = tasks.filter((task) => task.status === "focus" || task.status === "in_progress");
|
|
4802
4938
|
const recommendedNextTask = focusTasks[0] ??
|
|
4803
4939
|
tasks.find((task) => task.status === "backlog") ??
|
|
@@ -4807,7 +4943,7 @@ function buildOperatorContext(userIds) {
|
|
|
4807
4943
|
generatedAt: new Date().toISOString(),
|
|
4808
4944
|
activeProjects: activeProjects.slice(0, 8),
|
|
4809
4945
|
focusTasks: focusTasks.slice(0, 12),
|
|
4810
|
-
dueHabits,
|
|
4946
|
+
dueHabits: scopedHabits,
|
|
4811
4947
|
currentBoard: {
|
|
4812
4948
|
backlog: tasks.filter((task) => task.status === "backlog").slice(0, 20),
|
|
4813
4949
|
focus: tasks.filter((task) => task.status === "focus").slice(0, 20),
|
|
@@ -4817,10 +4953,16 @@ function buildOperatorContext(userIds) {
|
|
|
4817
4953
|
blocked: tasks.filter((task) => task.status === "blocked").slice(0, 20),
|
|
4818
4954
|
done: tasks.filter((task) => task.status === "done").slice(0, 20)
|
|
4819
4955
|
},
|
|
4820
|
-
recentActivity: listActivityEvents({
|
|
4821
|
-
|
|
4956
|
+
recentActivity: listActivityEvents({
|
|
4957
|
+
limit: 20,
|
|
4958
|
+
userIds: scopedUserIdsForReads
|
|
4959
|
+
}),
|
|
4960
|
+
recentTaskRuns: listTaskRuns({
|
|
4961
|
+
limit: 12,
|
|
4962
|
+
userIds: scopedUserIdsForReads
|
|
4963
|
+
}),
|
|
4822
4964
|
recommendedNextTask,
|
|
4823
|
-
xp: buildXpMetricsPayload()
|
|
4965
|
+
xp: buildXpMetricsPayload({ goals, tasks, habits: scopedHabits })
|
|
4824
4966
|
};
|
|
4825
4967
|
}
|
|
4826
4968
|
function buildUserDirectoryPayload() {
|
|
@@ -4948,7 +5090,13 @@ function buildOperatorOverviewRouteGuide() {
|
|
|
4948
5090
|
}
|
|
4949
5091
|
function buildOperatorOverview(request) {
|
|
4950
5092
|
const auth = parseRequestAuth(request.headers);
|
|
4951
|
-
const
|
|
5093
|
+
const readScope = resolveEffectiveReadScope(request.query, auth);
|
|
5094
|
+
const users = listUsers();
|
|
5095
|
+
const { scopedUserIdsForReads } = normalizeScopedUserIdsForReads({
|
|
5096
|
+
scope: readScope,
|
|
5097
|
+
validUserIds: users.map((user) => user.id)
|
|
5098
|
+
});
|
|
5099
|
+
const userIds = scopedUserIdsForReads;
|
|
4952
5100
|
const canReadPsyche = auth.token
|
|
4953
5101
|
? hasTokenScope(auth.token, "psyche.read")
|
|
4954
5102
|
: true;
|
|
@@ -4959,8 +5107,8 @@ function buildOperatorOverview(request) {
|
|
|
4959
5107
|
];
|
|
4960
5108
|
return {
|
|
4961
5109
|
generatedAt: new Date().toISOString(),
|
|
4962
|
-
snapshot: buildV1Context(
|
|
4963
|
-
operator: buildOperatorContext(
|
|
5110
|
+
snapshot: buildV1Context(readScope),
|
|
5111
|
+
operator: buildOperatorContext(readScope),
|
|
4964
5112
|
sleep: getSleepViewData(userIds),
|
|
4965
5113
|
domains: listDomains(),
|
|
4966
5114
|
psyche: canReadPsyche ? getPsycheOverview(userIds) : null,
|
|
@@ -5449,7 +5597,10 @@ export async function buildServer(options = {}) {
|
|
|
5449
5597
|
revoked: managers.session.revokeCurrentSession(request.headers, reply)
|
|
5450
5598
|
}));
|
|
5451
5599
|
app.get("/api/v1/openapi.json", async () => buildOpenApiDocument());
|
|
5452
|
-
app.get("/api/v1/context", async (request) =>
|
|
5600
|
+
app.get("/api/v1/context", async (request) => {
|
|
5601
|
+
const auth = authenticateRequest(request.headers);
|
|
5602
|
+
return buildV1Context(resolveEffectiveReadScope(request.query, auth));
|
|
5603
|
+
});
|
|
5453
5604
|
app.get("/api/v1/life-force", async (request) => ({
|
|
5454
5605
|
lifeForce: buildLifeForcePayload(new Date(), resolveScopedUserIds(request.query)),
|
|
5455
5606
|
templates: listLifeForceTemplates(resolveLifeForceUser(resolveScopedUserIds(request.query)).id)
|
|
@@ -6106,16 +6257,15 @@ export async function buildServer(options = {}) {
|
|
|
6106
6257
|
return { sleep };
|
|
6107
6258
|
});
|
|
6108
6259
|
app.get("/api/v1/operator/context", async (request) => {
|
|
6109
|
-
|
|
6260
|
+
const auth = requireAuthenticatedActor(request.headers, {
|
|
6110
6261
|
route: "/api/v1/operator/context"
|
|
6111
6262
|
});
|
|
6112
|
-
const userIds = resolveScopedUserIds(request.query);
|
|
6113
6263
|
return {
|
|
6114
|
-
context: buildOperatorContext(
|
|
6264
|
+
context: buildOperatorContext(resolveEffectiveReadScope(request.query, auth))
|
|
6115
6265
|
};
|
|
6116
6266
|
});
|
|
6117
6267
|
app.get("/api/v1/operator/overview", async (request) => {
|
|
6118
|
-
|
|
6268
|
+
requireAuthenticatedActor(request.headers, {
|
|
6119
6269
|
route: "/api/v1/operator/overview"
|
|
6120
6270
|
});
|
|
6121
6271
|
return {
|
|
@@ -39,6 +39,11 @@ export class AuthenticationManager extends AbstractManager {
|
|
|
39
39
|
actor,
|
|
40
40
|
source,
|
|
41
41
|
token,
|
|
42
|
+
scope: {
|
|
43
|
+
userIds: token?.scopePolicy.userIds ?? [],
|
|
44
|
+
projectIds: token?.scopePolicy.projectIds ?? [],
|
|
45
|
+
tagIds: token?.scopePolicy.tagIds ?? []
|
|
46
|
+
},
|
|
42
47
|
session
|
|
43
48
|
};
|
|
44
49
|
}
|
|
@@ -1825,6 +1825,43 @@ export function buildOpenApiDocument() {
|
|
|
1825
1825
|
}
|
|
1826
1826
|
}
|
|
1827
1827
|
};
|
|
1828
|
+
const agentBootstrapPolicy = {
|
|
1829
|
+
type: "object",
|
|
1830
|
+
additionalProperties: false,
|
|
1831
|
+
required: [
|
|
1832
|
+
"mode",
|
|
1833
|
+
"goalsLimit",
|
|
1834
|
+
"projectsLimit",
|
|
1835
|
+
"tasksLimit",
|
|
1836
|
+
"habitsLimit",
|
|
1837
|
+
"strategiesLimit",
|
|
1838
|
+
"peoplePageLimit",
|
|
1839
|
+
"includePeoplePages"
|
|
1840
|
+
],
|
|
1841
|
+
properties: {
|
|
1842
|
+
mode: {
|
|
1843
|
+
type: "string",
|
|
1844
|
+
enum: ["disabled", "active_only", "scoped", "full"]
|
|
1845
|
+
},
|
|
1846
|
+
goalsLimit: { type: "integer", minimum: 0, maximum: 100 },
|
|
1847
|
+
projectsLimit: { type: "integer", minimum: 0, maximum: 100 },
|
|
1848
|
+
tasksLimit: { type: "integer", minimum: 0, maximum: 100 },
|
|
1849
|
+
habitsLimit: { type: "integer", minimum: 0, maximum: 100 },
|
|
1850
|
+
strategiesLimit: { type: "integer", minimum: 0, maximum: 100 },
|
|
1851
|
+
peoplePageLimit: { type: "integer", minimum: 0, maximum: 50 },
|
|
1852
|
+
includePeoplePages: { type: "boolean" }
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
const agentScopePolicy = {
|
|
1856
|
+
type: "object",
|
|
1857
|
+
additionalProperties: false,
|
|
1858
|
+
required: ["userIds", "projectIds", "tagIds"],
|
|
1859
|
+
properties: {
|
|
1860
|
+
userIds: arrayOf({ type: "string" }),
|
|
1861
|
+
projectIds: arrayOf({ type: "string" }),
|
|
1862
|
+
tagIds: arrayOf({ type: "string" })
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1828
1865
|
const agentTokenSummary = {
|
|
1829
1866
|
type: "object",
|
|
1830
1867
|
additionalProperties: false,
|
|
@@ -1839,6 +1876,8 @@ export function buildOpenApiDocument() {
|
|
|
1839
1876
|
"autonomyMode",
|
|
1840
1877
|
"approvalMode",
|
|
1841
1878
|
"description",
|
|
1879
|
+
"bootstrapPolicy",
|
|
1880
|
+
"scopePolicy",
|
|
1842
1881
|
"lastUsedAt",
|
|
1843
1882
|
"revokedAt",
|
|
1844
1883
|
"createdAt",
|
|
@@ -1865,6 +1904,8 @@ export function buildOpenApiDocument() {
|
|
|
1865
1904
|
enum: ["approval_by_default", "high_impact_only", "none"]
|
|
1866
1905
|
},
|
|
1867
1906
|
description: { type: "string" },
|
|
1907
|
+
bootstrapPolicy: { $ref: "#/components/schemas/AgentBootstrapPolicy" },
|
|
1908
|
+
scopePolicy: { $ref: "#/components/schemas/AgentScopePolicy" },
|
|
1868
1909
|
lastUsedAt: nullable({ type: "string", format: "date-time" }),
|
|
1869
1910
|
revokedAt: nullable({ type: "string", format: "date-time" }),
|
|
1870
1911
|
createdAt: { type: "string", format: "date-time" },
|
|
@@ -2779,6 +2820,10 @@ export function buildOpenApiDocument() {
|
|
|
2779
2820
|
"recommendedTrustLevel",
|
|
2780
2821
|
"recommendedAutonomyMode",
|
|
2781
2822
|
"recommendedApprovalMode",
|
|
2823
|
+
"defaultBootstrapPolicy",
|
|
2824
|
+
"effectiveBootstrapPolicy",
|
|
2825
|
+
"defaultScopePolicy",
|
|
2826
|
+
"effectiveScopePolicy",
|
|
2782
2827
|
"authModes",
|
|
2783
2828
|
"tokenRecovery",
|
|
2784
2829
|
"requiredHeaders",
|
|
@@ -2827,6 +2872,18 @@ export function buildOpenApiDocument() {
|
|
|
2827
2872
|
type: "string",
|
|
2828
2873
|
enum: ["approval_by_default", "high_impact_only", "none"]
|
|
2829
2874
|
},
|
|
2875
|
+
defaultBootstrapPolicy: {
|
|
2876
|
+
$ref: "#/components/schemas/AgentBootstrapPolicy"
|
|
2877
|
+
},
|
|
2878
|
+
effectiveBootstrapPolicy: {
|
|
2879
|
+
$ref: "#/components/schemas/AgentBootstrapPolicy"
|
|
2880
|
+
},
|
|
2881
|
+
defaultScopePolicy: {
|
|
2882
|
+
$ref: "#/components/schemas/AgentScopePolicy"
|
|
2883
|
+
},
|
|
2884
|
+
effectiveScopePolicy: {
|
|
2885
|
+
$ref: "#/components/schemas/AgentScopePolicy"
|
|
2886
|
+
},
|
|
2830
2887
|
authModes: {
|
|
2831
2888
|
type: "object",
|
|
2832
2889
|
additionalProperties: false,
|
|
@@ -4172,6 +4229,8 @@ export function buildOpenApiDocument() {
|
|
|
4172
4229
|
AgentRuntimeSessionEvent: agentRuntimeSessionEvent,
|
|
4173
4230
|
AgentRuntimeSession: agentRuntimeSession,
|
|
4174
4231
|
AgentRuntimeSessionHistory: agentRuntimeSessionHistory,
|
|
4232
|
+
AgentBootstrapPolicy: agentBootstrapPolicy,
|
|
4233
|
+
AgentScopePolicy: agentScopePolicy,
|
|
4175
4234
|
AgentTokenSummary: agentTokenSummary,
|
|
4176
4235
|
AgentTokenMutationResult: agentTokenMutationResult,
|
|
4177
4236
|
Domain: domain,
|
|
@@ -7,7 +7,7 @@ import { recordActivityEvent } from "./activity-events.js";
|
|
|
7
7
|
import { recordEventLog } from "./event-log.js";
|
|
8
8
|
import { resolveGoogleCalendarOauthPublicConfig } from "../services/google-calendar-oauth-config.js";
|
|
9
9
|
import { buildConnectionAgentIdentity, FORGE_DEFAULT_AGENT_ID, listAiModelConnections, syncForgeManagedWikiProfile } from "./model-settings.js";
|
|
10
|
-
import { createAgentTokenSchema, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
|
|
10
|
+
import { agentBootstrapPolicySchema, agentScopePolicySchema, createAgentTokenSchema, legacyAgentBootstrapPolicy, defaultAgentScopePolicy, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
|
|
11
11
|
const settingsFileSchema = settingsPayloadSchema.deepPartial();
|
|
12
12
|
let settingsFileSyncDepth = 0;
|
|
13
13
|
let lastSettingsFileStatus = {
|
|
@@ -319,6 +319,12 @@ function mapAgent(row) {
|
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
321
|
function mapToken(row) {
|
|
322
|
+
const bootstrapPolicy = agentBootstrapPolicySchema.parse(row.bootstrap_policy_json
|
|
323
|
+
? JSON.parse(row.bootstrap_policy_json)
|
|
324
|
+
: legacyAgentBootstrapPolicy);
|
|
325
|
+
const scopePolicy = agentScopePolicySchema.parse(row.scope_policy_json
|
|
326
|
+
? JSON.parse(row.scope_policy_json)
|
|
327
|
+
: defaultAgentScopePolicy);
|
|
322
328
|
return {
|
|
323
329
|
id: row.id,
|
|
324
330
|
label: row.label,
|
|
@@ -330,6 +336,8 @@ function mapToken(row) {
|
|
|
330
336
|
autonomyMode: row.autonomy_mode,
|
|
331
337
|
approvalMode: row.approval_mode,
|
|
332
338
|
description: row.description,
|
|
339
|
+
bootstrapPolicy,
|
|
340
|
+
scopePolicy,
|
|
333
341
|
lastUsedAt: row.last_used_at,
|
|
334
342
|
revokedAt: row.revoked_at,
|
|
335
343
|
createdAt: row.created_at,
|
|
@@ -409,6 +417,8 @@ export function listAgentTokens() {
|
|
|
409
417
|
agent_tokens.label,
|
|
410
418
|
agent_tokens.token_prefix,
|
|
411
419
|
agent_tokens.scopes_json,
|
|
420
|
+
agent_tokens.bootstrap_policy_json,
|
|
421
|
+
agent_tokens.scope_policy_json,
|
|
412
422
|
agent_tokens.agent_id,
|
|
413
423
|
agent_identities.label AS agent_label,
|
|
414
424
|
agent_tokens.trust_level,
|
|
@@ -807,9 +817,9 @@ export function createAgentToken(input, activity) {
|
|
|
807
817
|
const tokenPrefix = `${token.slice(0, 10)}••••`;
|
|
808
818
|
getDatabase()
|
|
809
819
|
.prepare(`INSERT INTO agent_tokens (
|
|
810
|
-
id, label, token_hash, token_prefix, scopes_json, agent_id, trust_level, autonomy_mode, approval_mode, description, created_at, updated_at
|
|
811
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
812
|
-
.run(id, parsed.label, hashToken(token), tokenPrefix, JSON.stringify(parsed.scopes), agent.id, parsed.trustLevel, parsed.autonomyMode, parsed.approvalMode, parsed.description, now, now);
|
|
820
|
+
id, label, token_hash, token_prefix, scopes_json, bootstrap_policy_json, scope_policy_json, agent_id, trust_level, autonomy_mode, approval_mode, description, created_at, updated_at
|
|
821
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
822
|
+
.run(id, parsed.label, hashToken(token), tokenPrefix, JSON.stringify(parsed.scopes), JSON.stringify(parsed.bootstrapPolicy), JSON.stringify(parsed.scopePolicy), agent.id, parsed.trustLevel, parsed.autonomyMode, parsed.approvalMode, parsed.description, now, now);
|
|
813
823
|
const tokenSummary = listAgentTokens().find((entry) => entry.id === id);
|
|
814
824
|
if (activity) {
|
|
815
825
|
recordActivityEvent({
|
|
@@ -823,7 +833,11 @@ export function createAgentToken(input, activity) {
|
|
|
823
833
|
metadata: {
|
|
824
834
|
agentId: agent.id,
|
|
825
835
|
agentLabel: agent.label,
|
|
826
|
-
scopes: parsed.scopes.join(",")
|
|
836
|
+
scopes: parsed.scopes.join(","),
|
|
837
|
+
bootstrapMode: parsed.bootstrapPolicy.mode,
|
|
838
|
+
scopedUserIds: parsed.scopePolicy.userIds.join(","),
|
|
839
|
+
scopedProjectIds: parsed.scopePolicy.projectIds.join(","),
|
|
840
|
+
scopedTagIds: parsed.scopePolicy.tagIds.join(",")
|
|
827
841
|
}
|
|
828
842
|
});
|
|
829
843
|
recordEventLog({
|
|
@@ -836,7 +850,11 @@ export function createAgentToken(input, activity) {
|
|
|
836
850
|
agentId: agent.id,
|
|
837
851
|
trustLevel: parsed.trustLevel,
|
|
838
852
|
autonomyMode: parsed.autonomyMode,
|
|
839
|
-
approvalMode: parsed.approvalMode
|
|
853
|
+
approvalMode: parsed.approvalMode,
|
|
854
|
+
bootstrapMode: parsed.bootstrapPolicy.mode,
|
|
855
|
+
scopeUserCount: parsed.scopePolicy.userIds.length,
|
|
856
|
+
scopeProjectCount: parsed.scopePolicy.projectIds.length,
|
|
857
|
+
scopeTagCount: parsed.scopePolicy.tagIds.length
|
|
840
858
|
}
|
|
841
859
|
});
|
|
842
860
|
}
|
|
@@ -929,6 +947,8 @@ export function verifyAgentToken(token) {
|
|
|
929
947
|
agent_tokens.label,
|
|
930
948
|
agent_tokens.token_prefix,
|
|
931
949
|
agent_tokens.scopes_json,
|
|
950
|
+
agent_tokens.bootstrap_policy_json,
|
|
951
|
+
agent_tokens.scope_policy_json,
|
|
932
952
|
agent_tokens.agent_id,
|
|
933
953
|
agent_identities.label AS agent_label,
|
|
934
954
|
agent_tokens.trust_level,
|