fuli-context 0.7.4-beta.3 → 0.7.6

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 (130) hide show
  1. package/README.md +77 -6
  2. package/README.zh-CN.md +69 -6
  3. package/compose.graphiti.yml +6 -6
  4. package/dist/web/assets/AboutPage-sLV9WChi.js +1 -0
  5. package/dist/web/assets/ConnectionsPage-BWn4ywGJ.js +1 -0
  6. package/dist/web/assets/KnowledgeEditDialog-CZ5ziPrD.js +1 -0
  7. package/dist/web/assets/KnowledgeInspector-CRQWOaos.js +1 -0
  8. package/dist/web/assets/KnowledgeOrganizerPage-Dfj3vIpU.js +1 -0
  9. package/dist/web/assets/KnowledgePage-BRYC9S5Y.js +1 -0
  10. package/dist/web/assets/KnowledgeWorkspace-ChEB5Bwk.js +4 -0
  11. package/dist/web/assets/OverviewPage-Cd66M0RT.js +1 -0
  12. package/dist/web/assets/PersonalProfilePage-RCFHL0Wz.js +1 -0
  13. package/dist/web/assets/PersonalProjectsPage-DEh8XWby.js +1 -0
  14. package/dist/web/assets/ProjectAgentsPage-Crh7PrXa.css +1 -0
  15. package/dist/web/assets/ProjectAgentsPage-DPnaH-TR.js +11 -0
  16. package/dist/web/assets/PublicProjectsPage-mstPEjHe.js +1 -0
  17. package/dist/web/assets/ReviewPage-6Y1SmfZy.js +1 -0
  18. package/dist/web/assets/SearchableSelect-D3HM8YF8.js +1 -0
  19. package/dist/web/assets/SettingsPage--LdTMUm7.js +1 -0
  20. package/dist/web/assets/SettingsPage-CwZp2ka4.css +1 -0
  21. package/dist/web/assets/WritingTastePage-CIpKswvs.js +1 -0
  22. package/dist/web/assets/{console-D_2zC4lm.js → console-BT1P2Db4.js} +1 -1
  23. package/dist/web/assets/{conversation-launcher-settings-DQmMH2j7.js → conversation-launcher-settings-DrJEotdf.js} +1 -1
  24. package/dist/web/assets/i18n-js6gfc7j.js +9 -0
  25. package/dist/web/assets/{identity-DmGcY3Y6.js → identity-CfDfanpa.js} +1 -1
  26. package/dist/web/assets/index-BnH5mcZx.css +1 -0
  27. package/dist/web/assets/index-GqJf1xqg.js +2 -0
  28. package/dist/web/assets/{model-CVoOdRsL.js → model-DJkLAJsg.js} +1 -1
  29. package/dist/web/assets/{useApi-CROJJdhE-CBNg4CdH.js → useApi-CROJJdhE-BdSti7Y2.js} +1 -1
  30. package/dist/web/assets/vue-router-Kk67EaCP.js +1 -0
  31. package/dist/web/index.html +6 -6
  32. package/graph-provider/fuli_graph/app.py +8 -0
  33. package/graph-provider/fuli_graph/collaboration_context.py +69 -6
  34. package/graph-provider/fuli_graph/knowledge_search.py +84 -5
  35. package/graph-provider/fuli_graph/model_validation.py +130 -0
  36. package/graph-provider/fuli_graph/models.py +24 -5
  37. package/graph-provider/fuli_graph/project_agent_access.py +54 -0
  38. package/graph-provider/fuli_graph/project_agent_coordination_models.py +22 -0
  39. package/graph-provider/fuli_graph/project_agent_executor_models.py +635 -0
  40. package/graph-provider/fuli_graph/project_agent_models.py +278 -0
  41. package/graph-provider/fuli_graph/project_agent_routes.py +626 -0
  42. package/graph-provider/fuli_graph/project_agent_task_models.py +545 -0
  43. package/graph-provider/fuli_graph/runtime.py +259 -0
  44. package/graph-provider/fuli_graph/search_projection.py +3 -0
  45. package/graph-provider/fuli_graph/store.py +6 -0
  46. package/graph-provider/fuli_graph/store_knowledge.py +62 -9
  47. package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +122 -0
  48. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +756 -0
  49. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
  50. package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
  51. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
  52. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +705 -0
  53. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +586 -0
  54. package/graph-provider/fuli_graph/store_project_agent_tasks.py +1151 -0
  55. package/graph-provider/fuli_graph/store_project_agents.py +965 -0
  56. package/graph-provider/pyproject.toml +2 -1
  57. package/npm-shrinkwrap.json +2 -2
  58. package/package.json +1 -1
  59. package/skills/capturing-session-knowledge/SKILL.md +39 -0
  60. package/src/adaptive-runtime/executor-pool.js +127 -0
  61. package/src/adaptive-runtime/graph-services.js +166 -0
  62. package/src/adaptive-runtime/lease-client.js +335 -0
  63. package/src/adaptive-runtime/runtime-broker.js +318 -0
  64. package/src/adaptive-runtime/settings.js +111 -0
  65. package/src/adaptive-runtime/state.js +74 -0
  66. package/src/agent-tools/graph-definitions.js +419 -0
  67. package/src/agent-tools/graph-handlers.js +65 -0
  68. package/src/agent-tools/project-agent-definitions.js +307 -0
  69. package/src/backup/graph-backup.js +289 -0
  70. package/src/backup/identity-reconciliation.js +208 -0
  71. package/src/backup/runtime-adapters.js +269 -0
  72. package/src/cli/command-registry.js +6 -2
  73. package/src/cli/graph-data-command.js +112 -0
  74. package/src/cli/local-runtime-command.js +16 -2
  75. package/src/cli/setup-command.js +19 -3
  76. package/src/cli/update-command.js +4 -0
  77. package/src/cli.js +6 -0
  78. package/src/graphiti/collaboration-preference-projection.js +29 -1
  79. package/src/graphiti/collaboration-preference-workflow.js +121 -0
  80. package/src/graphiti/federated-application.js +53 -90
  81. package/src/graphiti/project-agent-control-plane.js +275 -0
  82. package/src/graphiti/project-agent-mapping.js +1024 -0
  83. package/src/graphiti/project-agent-workflows.js +576 -0
  84. package/src/graphiti/provider-client.js +282 -1
  85. package/src/http/api-router.js +63 -9
  86. package/src/http/graph-api-router.js +290 -0
  87. package/src/local-runtime/lifecycle.js +46 -10
  88. package/src/mcp/context-resources.js +15 -6
  89. package/src/mcp/create-mcp-server.js +51 -16
  90. package/src/mcp/instructions.js +7 -5
  91. package/src/mcp/runtime.js +17 -5
  92. package/src/mcp/session-id.js +38 -1
  93. package/src/mcp/tool-annotations.js +71 -0
  94. package/src/mcp-server.js +23 -6
  95. package/src/native-runtime/runtime.js +717 -0
  96. package/src/server.js +6 -2
  97. package/src/setup/agent-installation-status.js +7 -1
  98. package/src/setup/agents.js +3 -1
  99. package/src/setup/claude-code-config.js +3 -1
  100. package/src/setup/codex-bootstrap.js +11 -10
  101. package/src/setup/codex-config.js +7 -1
  102. package/src/setup/cursor-config.js +3 -1
  103. package/src/setup/graph-runtime.js +158 -13
  104. package/src/setup/neo4j-memory-profile.js +43 -0
  105. package/src/setup/options.js +38 -2
  106. package/src/setup/paths.js +14 -0
  107. package/src/setup/setup.js +37 -1
  108. package/src/system/resource-monitor.js +181 -16
  109. package/src/system/runtime-settings.js +10 -1
  110. package/src/system/system-service.js +27 -2
  111. package/dist/web/assets/AboutPage-Dpf5fRWv.js +0 -1
  112. package/dist/web/assets/ConnectionsPage-D21aUDCP.js +0 -1
  113. package/dist/web/assets/KnowledgeEditDialog-u7lpfN3j.js +0 -1
  114. package/dist/web/assets/KnowledgeInspector-k4xnHtXd.js +0 -1
  115. package/dist/web/assets/KnowledgeOrganizerPage-Bdj7Rn84.js +0 -1
  116. package/dist/web/assets/KnowledgePage-UHVAbC1W.js +0 -1
  117. package/dist/web/assets/KnowledgeWorkspace-Lq3d-MeC.js +0 -4
  118. package/dist/web/assets/OverviewPage-CycAgA1V.js +0 -1
  119. package/dist/web/assets/PersonalProfilePage-U9oIV5FT.js +0 -1
  120. package/dist/web/assets/PersonalProjectsPage-C5RpXKcA.js +0 -1
  121. package/dist/web/assets/PublicProjectsPage-DiWxm35E.js +0 -1
  122. package/dist/web/assets/ReviewPage-jxRfGSJK.js +0 -1
  123. package/dist/web/assets/SearchableSelect-B3-5IPup.js +0 -1
  124. package/dist/web/assets/SettingsPage-BqfGDOho.js +0 -1
  125. package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
  126. package/dist/web/assets/WritingTastePage-BJxhMqJR.js +0 -1
  127. package/dist/web/assets/i18n-CwqXDI1C.js +0 -5
  128. package/dist/web/assets/index-IUGLt4lG.js +0 -2
  129. package/dist/web/assets/index-X2j29j8i.css +0 -1
  130. package/dist/web/assets/vue-router-CWSsUUio.js +0 -1
package/src/cli.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  printHelp
8
8
  } from './cli/command-registry.js';
9
9
  import { runLocalRuntimeCommand } from './cli/local-runtime-command.js';
10
+ import { runGraphDataCommand } from './cli/graph-data-command.js';
10
11
  import { runSetupCommand } from './cli/setup-command.js';
11
12
  import { runUninstallCommand } from './cli/uninstall-command.js';
12
13
  import { runUpdateCommand } from './cli/update-command.js';
@@ -29,6 +30,11 @@ export async function main(argv = process.argv.slice(2), env = process.env) {
29
30
  await runSetupCommand(commandArgs, { env });
30
31
  return;
31
32
  }
33
+ if (command === 'graph') {
34
+ assertSupportedNodeVersion();
35
+ const [action, ...graphArgs] = commandArgs;
36
+ return runGraphDataCommand(action, graphArgs, { env });
37
+ }
32
38
  if (command === 'connect-workspace') {
33
39
  assertSupportedNodeVersion();
34
40
  return runWorkspaceConnectionCommand(commandArgs, { env });
@@ -10,6 +10,9 @@ export function agentCollaborationPreference(item) {
10
10
  if (item.preference_project_id) {
11
11
  preference.preference_project_id = item.preference_project_id;
12
12
  }
13
+ if (item.preference_agent_id) {
14
+ preference.preference_agent_id = item.preference_agent_id;
15
+ }
13
16
  for (const field of [
14
17
  'attributes',
15
18
  'weight',
@@ -27,10 +30,31 @@ export function agentCollaborationPreference(item) {
27
30
  return preference;
28
31
  }
29
32
 
33
+ export function activePreferenceConflicts(result) {
34
+ const items = [
35
+ ...(result.global_preferences ?? []),
36
+ ...(result.project_preferences ?? []),
37
+ ...(result.agent_preferences ?? [])
38
+ ];
39
+ const itemById = new Map(items.map((item) => [item.id, item]));
40
+ return (result.conflicts ?? []).map((conflict) => ({
41
+ preference_key: conflict.preference_key,
42
+ preference_scope: conflict.preference_scope,
43
+ preference_project_id: conflict.preference_project_id ?? null,
44
+ preference_agent_id: conflict.preference_agent_id ?? null,
45
+ alternatives: (conflict.item_ids ?? [])
46
+ .map((id) => itemById.get(id))
47
+ .filter(Boolean)
48
+ .map(agentCollaborationPreference),
49
+ required_action: 'Do not apply any alternative until the conflict is resolved.'
50
+ }));
51
+ }
52
+
30
53
  export function deferredPreferenceConflicts(result, queuedConflicts) {
31
54
  const items = [
32
55
  ...(result.global_preferences ?? []),
33
- ...(result.project_preferences ?? [])
56
+ ...(result.project_preferences ?? []),
57
+ ...(result.agent_preferences ?? [])
34
58
  ];
35
59
  const itemById = new Map(items.map((item) => [item.id, item]));
36
60
  const activeConflictPairs = new Set(
@@ -121,6 +145,9 @@ export function agentDeferredPreferenceConflict(conflict) {
121
145
  ...(conflict.preference_project_id
122
146
  ? { preference_project_id: conflict.preference_project_id }
123
147
  : {}),
148
+ ...(conflict.preference_agent_id
149
+ ? { preference_agent_id: conflict.preference_agent_id }
150
+ : {}),
124
151
  status: conflict.status,
125
152
  deferred_at: conflict.deferred_at,
126
153
  reason: conflict.reason,
@@ -142,6 +169,7 @@ function agentConflictPreference(item) {
142
169
  };
143
170
  for (const field of [
144
171
  'preference_project_id',
172
+ 'preference_agent_id',
145
173
  'inherited_from_project_id',
146
174
  'scope_distance',
147
175
  'scope_path',
@@ -0,0 +1,121 @@
1
+ import {
2
+ agentProjectResolution
3
+ } from './agent-knowledge-workflows.js';
4
+ import {
5
+ activePreferenceConflicts,
6
+ agentCollaborationPreference,
7
+ agentDeferredPreferenceConflict,
8
+ deferredPreferenceConflicts
9
+ } from './collaboration-preference-projection.js';
10
+ import {
11
+ recallTaskKnowledge,
12
+ TASK_KNOWLEDGE_RETRIEVAL_GUIDANCE
13
+ } from './task-knowledge-recall.js';
14
+
15
+ export async function getCollaborationPreferences(
16
+ application,
17
+ projectResolution,
18
+ {
19
+ projectAgentId = null,
20
+ taskPrompt = null,
21
+ limit = 100,
22
+ agentInvocation = false,
23
+ agentToolName = 'get_collaboration_preferences'
24
+ },
25
+ recordAgentViews
26
+ ) {
27
+ const resolvedProjectId = projectResolution.personalProjectId;
28
+ const [result, queuedConflicts] = await Promise.all([
29
+ application.personal.collaborationPreferences(
30
+ application.config.personal.spaceId,
31
+ resolvedProjectId,
32
+ limit,
33
+ projectAgentId
34
+ ),
35
+ application.personal.listPreferenceConflicts(
36
+ application.config.personal.spaceId,
37
+ 'ai_pending',
38
+ limit
39
+ )
40
+ ]);
41
+ const deferredConflicts = deferredPreferenceConflicts(
42
+ result,
43
+ queuedConflicts
44
+ );
45
+ const taskKnowledgeRecall = taskPrompt === null
46
+ ? null
47
+ : await recallTaskKnowledge(application, projectResolution, taskPrompt);
48
+ if (agentInvocation) {
49
+ const items = [
50
+ ...(result.global_preferences ?? []),
51
+ ...(result.project_preferences ?? []),
52
+ ...(result.agent_preferences ?? [])
53
+ ];
54
+ await recordAgentViews(items.map(({ id, item_kind: itemKind }) => ({
55
+ item_id: id,
56
+ item_kind: itemKind
57
+ })), agentToolName);
58
+ }
59
+ const applicationGuidance = guidanceFor(
60
+ resolvedProjectId,
61
+ projectAgentId
62
+ );
63
+ if (!agentInvocation) {
64
+ return {
65
+ ...result,
66
+ deferred_conflicts: deferredConflicts,
67
+ application_guidance: applicationGuidance,
68
+ project_resolution: agentProjectResolution(projectResolution),
69
+ ...(taskKnowledgeRecall ? { task_knowledge_recall: taskKnowledgeRecall } : {})
70
+ };
71
+ }
72
+ return {
73
+ effective_preferences: (result.effective_preferences ?? [])
74
+ .map(agentCollaborationPreference),
75
+ deferred_conflicts: deferredConflicts.map(agentDeferredPreferenceConflict),
76
+ active_conflicts: activePreferenceConflicts(result),
77
+ application_guidance: applicationGuidance,
78
+ ...(taskKnowledgeRecall ? { task_knowledge_recall: taskKnowledgeRecall } : {}),
79
+ context: preferenceContext(result, deferredConflicts, projectResolution)
80
+ };
81
+ }
82
+
83
+ function guidanceFor(projectId, projectAgentId) {
84
+ return {
85
+ apply: 'effective_preferences',
86
+ global_scope: 'Apply personal-global preferences in every user task.',
87
+ project_scope: projectId
88
+ ? `Also apply preferences scoped to ${projectId} and confirmed ancestor preferences whose descendants or selected-projects mode explicitly reaches it.`
89
+ : 'No exact personal project matched; do not apply project-scoped preferences.',
90
+ agent_scope: projectAgentId
91
+ ? `Also apply preferences scoped to project Agent ${projectAgentId}; exclude every other project Agent.`
92
+ : 'No project Agent was selected; do not apply Agent-scoped preferences.',
93
+ inheritance: 'Project preference inheritance follows only PART_OF or USES_KNOWLEDGE_FROM for at most two hops. Preserve inherited_from_project_id, scope_path, and scope_distance. An exact item wins only after confirmation authority is compared; weight never expands scope or resolves a conflict.',
94
+ related_projects: 'RELATED_TO preferences never auto-apply. Use structured related-project suggestions to ask before a one-time read-only expansion.',
95
+ conflicts: 'Do not apply entries listed in conflicts until the user resolves them.',
96
+ deferred_conflicts: 'If the current task would use a deferred_conflict, call resolve_deferred_preference_conflict before applying either side. Ignore unrelated deferred conflicts. The resolution must preserve the AI audit marker.',
97
+ authority: 'Human or authoritative-source confirmed preferences outrank agent-confirmed preferences. Agent-confirmed preferences are usable but lower priority and remain explicitly marked.',
98
+ pending: 'Pending preferences are available only through on-demand knowledge search; invalid and unrelated-project preferences are excluded. Automatic preference injection does not count as usage evidence.',
99
+ knowledge_retrieval: TASK_KNOWLEDGE_RETRIEVAL_GUIDANCE
100
+ };
101
+ }
102
+
103
+ function preferenceContext(result, deferredConflicts, projectResolution) {
104
+ return {
105
+ personal_space_id: result.personal_space_id,
106
+ personal_project_id: result.personal_project_id,
107
+ project_agent_id: result.project_agent_id ?? null,
108
+ global_preference_count: (result.global_preferences ?? []).length,
109
+ project_preference_count: (result.project_preferences ?? []).length,
110
+ inherited_project_preference_count: (result.project_preferences ?? [])
111
+ .filter(({ inherited_from_project_id: projectId }) => Boolean(projectId))
112
+ .length,
113
+ agent_preference_count: (result.agent_preferences ?? []).length,
114
+ conflict_count: (result.conflicts ?? []).length,
115
+ ai_deferred_conflict_count: deferredConflicts.length,
116
+ overridden_global_count: (result.overridden_global_ids ?? []).length,
117
+ overridden_project_count: (result.overridden_project_ids ?? []).length,
118
+ source_truncated: result.truncated === true,
119
+ project_resolution: agentProjectResolution(projectResolution)
120
+ };
121
+ }
@@ -28,17 +28,12 @@ import {
28
28
  createFuliSourceMarker,
29
29
  sourceConsoleUrl
30
30
  } from './source-marker.js';
31
- import {
32
- recallTaskKnowledge,
33
- TASK_KNOWLEDGE_RETRIEVAL_GUIDANCE
34
- } from './task-knowledge-recall.js';
35
31
  import { resolvePersonalProjectPath } from './project-path-context.js';
36
32
  import { mergeExternalKnowledgeProjection } from '../external-knowledge/graph-projection.js';
37
33
  import { TaskContextRegistry } from '../mcp/task-context-registry.js';
38
34
  import { attachExternalKnowledgeRuntime } from '../external-knowledge/runtime.js';
39
35
  import { resolveSetupPaths } from '../setup/paths.js';
40
36
  import {
41
- agentProjectResolution,
42
37
  beginTaskContext as beginTaskContextWorkflow,
43
38
  checkpointTaskKnowledge as checkpointTaskKnowledgeWorkflow,
44
39
  discoverCommonKnowledgeCandidates as discoverCommonKnowledgeCandidatesWorkflow,
@@ -60,6 +55,10 @@ import {
60
55
  providerEpisode
61
56
  } from './knowledge-provider-mapping.js';
62
57
  import { providerProjectProfile } from './project-profile-mapping.js';
58
+ import {
59
+ ProjectAgentControlPlaneApplication,
60
+ projectAgentControlPlaneHooks
61
+ } from './project-agent-control-plane.js';
63
62
  import {
64
63
  finishKnowledgeReview as finishKnowledgeReviewWorkflow,
65
64
  listKnowledgeReviewCandidates as listKnowledgeReviewCandidatesWorkflow,
@@ -77,10 +76,8 @@ import {
77
76
  relatedProjectGuidance
78
77
  } from './related-project-suggestions.js';
79
78
  import {
80
- agentCollaborationPreference,
81
- agentDeferredPreferenceConflict,
82
- deferredPreferenceConflicts
83
- } from './collaboration-preference-projection.js';
79
+ getCollaborationPreferences as getCollaborationPreferencesWorkflow
80
+ } from './collaboration-preference-workflow.js';
84
81
  import { buildUserTasteSkill } from './user-taste-skill.js';
85
82
  import { getWritingTasteProfile as getWritingTasteProfileWorkflow } from './writing-taste-profile-workflow.js';
86
83
  import {
@@ -133,7 +130,7 @@ function providerRequestTimeoutFromEnv(env) {
133
130
  return parsed;
134
131
  }
135
132
 
136
- export class FederatedGraphApplication {
133
+ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplication {
137
134
  constructor(config, {
138
135
  fetchImpl = globalThis.fetch,
139
136
  capturePolicyStore = new CapturePolicyStore(),
@@ -143,6 +140,7 @@ export class FederatedGraphApplication {
143
140
  taskContextRegistry = new TaskContextRegistry(),
144
141
  providerRequestTimeoutMs = undefined
145
142
  } = {}) {
143
+ super();
146
144
  this.graphiti = true;
147
145
  this.config = config;
148
146
  this.capturePolicyStore = capturePolicyStore;
@@ -203,10 +201,17 @@ export class FederatedGraphApplication {
203
201
  const result = await this.personal.commit({
204
202
  space_id: input.spaceId,
205
203
  personal_project_id: input.personalProjectId ?? null,
204
+ project_agent_id: input.projectAgentId ?? null,
206
205
  episode
207
206
  });
208
207
  return { route: 'personal', ...result };
209
208
  }
209
+ if (input.projectAgentId) {
210
+ throw new ApplicationError(
211
+ ApplicationErrorCode.VALIDATION,
212
+ 'Project Agent knowledge can only be captured in the personal graph'
213
+ );
214
+ }
210
215
  if (episode.sensitivity !== 'normal') {
211
216
  throw new ApplicationError(
212
217
  ApplicationErrorCode.VALIDATION,
@@ -249,6 +254,7 @@ export class FederatedGraphApplication {
249
254
 
250
255
  async getCollaborationPreferences({
251
256
  personalProjectId = null,
257
+ projectAgentId = null,
252
258
  projectPath = null,
253
259
  taskPrompt = null,
254
260
  limit = 100,
@@ -259,80 +265,18 @@ export class FederatedGraphApplication {
259
265
  personalProjectId,
260
266
  projectPath
261
267
  });
262
- const resolvedProjectId = projectResolution.personalProjectId;
263
- const [result, queuedConflicts] = await Promise.all([
264
- this.personal.collaborationPreferences(
265
- this.config.personal.spaceId,
266
- resolvedProjectId,
267
- limit
268
- ),
269
- this.personal.listPreferenceConflicts(
270
- this.config.personal.spaceId,
271
- 'ai_pending',
272
- limit
273
- )
274
- ]);
275
- const deferredConflicts = deferredPreferenceConflicts(
276
- result,
277
- queuedConflicts
268
+ return getCollaborationPreferencesWorkflow(
269
+ this,
270
+ projectResolution,
271
+ {
272
+ projectAgentId,
273
+ taskPrompt,
274
+ limit,
275
+ agentInvocation,
276
+ agentToolName
277
+ },
278
+ (items, toolName) => this.#recordAgentViews(items, toolName)
278
279
  );
279
- const taskKnowledgeRecall = taskPrompt === null
280
- ? null
281
- : await recallTaskKnowledge(this, projectResolution, taskPrompt);
282
- if (agentInvocation) {
283
- const items = [
284
- ...(result.global_preferences ?? []),
285
- ...(result.project_preferences ?? [])
286
- ];
287
- await this.#recordAgentViews(items.map(({ id, item_kind: itemKind }) => ({
288
- item_id: id,
289
- item_kind: itemKind
290
- })), agentToolName);
291
- }
292
- const applicationGuidance = {
293
- apply: 'effective_preferences',
294
- global_scope: 'Apply personal-global preferences in every user task.',
295
- project_scope: resolvedProjectId
296
- ? `Also apply preferences scoped to ${resolvedProjectId} and confirmed ancestor preferences whose descendants or selected-projects mode explicitly reaches it.`
297
- : 'No exact personal project matched; do not apply project-scoped preferences.',
298
- inheritance: 'Project preference inheritance follows only PART_OF or USES_KNOWLEDGE_FROM for at most two hops. Preserve inherited_from_project_id, scope_path, and scope_distance. An exact item wins only after confirmation authority is compared; weight never expands scope or resolves a conflict.',
299
- related_projects: 'RELATED_TO preferences never auto-apply. Use structured related-project suggestions to ask before a one-time read-only expansion.',
300
- conflicts: 'Do not apply entries listed in conflicts until the user resolves them.',
301
- deferred_conflicts: 'If the current task would use a deferred_conflict, call resolve_deferred_preference_conflict before applying either side. Ignore unrelated deferred conflicts. The resolution must preserve the AI audit marker.',
302
- authority: 'Human or authoritative-source confirmed preferences outrank agent-confirmed preferences. Agent-confirmed preferences are usable but lower priority and remain explicitly marked.',
303
- pending: 'Pending preferences are available only through on-demand knowledge search; invalid and unrelated-project preferences are excluded. Automatic preference injection never counts as usage evidence.',
304
- knowledge_retrieval: TASK_KNOWLEDGE_RETRIEVAL_GUIDANCE
305
- };
306
- if (!agentInvocation) {
307
- return {
308
- ...result,
309
- deferred_conflicts: deferredConflicts,
310
- application_guidance: applicationGuidance,
311
- project_resolution: agentProjectResolution(projectResolution),
312
- ...(taskKnowledgeRecall ? { task_knowledge_recall: taskKnowledgeRecall } : {})
313
- };
314
- }
315
- return {
316
- effective_preferences: (result.effective_preferences ?? [])
317
- .map(agentCollaborationPreference),
318
- deferred_conflicts: deferredConflicts.map(agentDeferredPreferenceConflict),
319
- application_guidance: applicationGuidance,
320
- ...(taskKnowledgeRecall ? { task_knowledge_recall: taskKnowledgeRecall } : {}),
321
- context: {
322
- personal_space_id: result.personal_space_id,
323
- personal_project_id: result.personal_project_id,
324
- global_preference_count: (result.global_preferences ?? []).length,
325
- project_preference_count: (result.project_preferences ?? []).length,
326
- inherited_project_preference_count: (result.project_preferences ?? [])
327
- .filter(({ inherited_from_project_id: projectId }) => Boolean(projectId))
328
- .length,
329
- conflict_count: (result.conflicts ?? []).length,
330
- ai_deferred_conflict_count: deferredConflicts.length,
331
- overridden_global_count: (result.overridden_global_ids ?? []).length,
332
- source_truncated: result.truncated === true,
333
- project_resolution: agentProjectResolution(projectResolution)
334
- }
335
- };
336
280
  }
337
281
 
338
282
  async getUserTasteSkill({
@@ -415,6 +359,7 @@ export class FederatedGraphApplication {
415
359
  query,
416
360
  projectIds = [],
417
361
  personalProjectId = null,
362
+ projectAgentId = null,
418
363
  contextPersonalProjectIds = [],
419
364
  personalProjectScope = 'bounded',
420
365
  limit = 12,
@@ -429,6 +374,11 @@ export class FederatedGraphApplication {
429
374
  if (!['bounded', 'all_local_confirmed'].includes(personalProjectScope)) {
430
375
  throw new TypeError('Unknown personal project search scope');
431
376
  }
377
+ if (projectAgentId && (!personalProjectId || personalProjectScope !== 'bounded')) {
378
+ throw new TypeError(
379
+ 'Project Agent search requires one bounded active personal project'
380
+ );
381
+ }
432
382
  const subscriptions = projectIds.length
433
383
  ? await this.personal.listSubscriptions(personalSpaceId)
434
384
  : [];
@@ -447,21 +397,25 @@ export class FederatedGraphApplication {
447
397
  )
448
398
  : personalContextProjectIds(personalProjectId, contextPersonalProjectIds);
449
399
  const personalSearches = personalProjectSearchBatches(selectedPersonalProjectIds)
450
- .map((personalProjectIds, index) => this.personal.search({
400
+ .map((personalProjectIds, index) => {
401
+ const activePersonalProjectId = (
402
+ personalProjectScope === 'bounded' &&
403
+ personalProjectId &&
404
+ personalProjectIds.includes(personalProjectId)
405
+ ) ? personalProjectId : null;
406
+ return this.personal.search({
451
407
  space_ids: [personalSpaceId],
452
408
  query,
453
409
  limit,
454
410
  include_historical: includeHistorical,
455
411
  include_exploratory: includePending,
456
412
  personal_project_ids: personalProjectIds,
457
- active_personal_project_id: (
458
- personalProjectScope === 'bounded' &&
459
- personalProjectId &&
460
- personalProjectIds.includes(personalProjectId)
461
- ) ? personalProjectId : null,
413
+ active_personal_project_id: activePersonalProjectId,
414
+ project_agent_id: activePersonalProjectId ? projectAgentId : null,
462
415
  inherit_project_knowledge: personalProjectScope === 'bounded',
463
416
  include_personal_global: index === 0
464
- }));
417
+ });
418
+ });
465
419
  const grouped = groupSubscriptions(selectedSubscriptions);
466
420
  const workspaceSearches = [...grouped.entries()].map(([providerUrl, items]) => {
467
421
  const projectIdsForProvider = items.map(({ project_id: projectId }) => projectId);
@@ -549,6 +503,7 @@ export class FederatedGraphApplication {
549
503
  entities,
550
504
  personalGlobalIncluded: true,
551
505
  personalProjectScope,
506
+ projectAgentId,
552
507
  searchedPersonalProjectIds: selectedPersonalProjectIds,
553
508
  requestedProjectIds,
554
509
  searchedProjectIds,
@@ -1232,6 +1187,14 @@ export class FederatedGraphApplication {
1232
1187
  }
1233
1188
  }
1234
1189
 
1190
+ [projectAgentControlPlaneHooks.assertActivePersonalSpace](personalSpaceId) {
1191
+ this.#assertActivePersonalSpace(personalSpaceId);
1192
+ }
1193
+
1194
+ async [projectAgentControlPlaneHooks.resolvePreferenceProject](input) {
1195
+ return this.#resolvePreferenceProject(input);
1196
+ }
1197
+
1235
1198
  #workspace(providerUrl) {
1236
1199
  const requested = providerUrl
1237
1200
  ? canonicalProviderUrl(providerUrl)