fuli-context 0.20.0-beta.2 → 0.20.0-beta.3

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 (561) hide show
  1. package/README.md +81 -22
  2. package/README.zh-CN.md +26 -12
  3. package/dist/web/assets/AboutPage-BkpqvBhp.css +1 -0
  4. package/dist/web/assets/AboutPage-BmX7l4yh.js +1 -0
  5. package/dist/web/assets/AgentName-DeHNVQ-r.js +1 -0
  6. package/dist/web/assets/AgentProfilePage-0ksQ58aV.css +1 -0
  7. package/dist/web/assets/AgentProfilePage-DbzxN3rz.js +1 -0
  8. package/dist/web/assets/AgentsDirectoryPage-DnvNg4s-.js +1 -0
  9. package/dist/web/assets/AgentsDirectoryPage-DoOXStj-.css +1 -0
  10. package/dist/web/assets/ConnectionsPage-C_-t36vt.js +1 -0
  11. package/dist/web/assets/CreateProjectDialog-78Sz76lL.js +2 -0
  12. package/dist/web/assets/CreateProjectDialog-t947qHxl.css +1 -0
  13. package/dist/web/assets/EmployeeRecruitDialog-BPT132zr.css +1 -0
  14. package/dist/web/assets/EmployeeRecruitDialog-CbakSlIK.js +1 -0
  15. package/dist/web/assets/EmployeeWorkbenchPage-BFSzzB5b.css +1 -0
  16. package/dist/web/assets/EmployeeWorkbenchPage-CZb36N9r.js +3 -0
  17. package/dist/web/assets/InboxPage-Bsa1NRSD.js +4 -0
  18. package/dist/web/assets/InboxPage-DiIA1trj.css +1 -0
  19. package/dist/web/assets/KnowledgeEditDialog-BXS3rlWg.css +1 -0
  20. package/dist/web/assets/KnowledgeEditDialog-CSjtslJS.js +1 -0
  21. package/dist/web/assets/KnowledgeInspector-DB9USgC0.js +1 -0
  22. package/dist/web/assets/KnowledgeInspector-DaknAv7P.css +1 -0
  23. package/dist/web/assets/KnowledgeOrganizerPage-CxICN_0p.css +1 -0
  24. package/dist/web/assets/KnowledgeOrganizerPage-DEucKWd7.js +1 -0
  25. package/dist/web/assets/KnowledgePage-tLziTkSU.js +1 -0
  26. package/dist/web/assets/KnowledgeWorkspace-B7ZiPf6n.js +4 -0
  27. package/dist/web/assets/KnowledgeWorkspace-BY_OoTDG.css +1 -0
  28. package/dist/web/assets/NotFoundPage-XPalsChA.js +1 -0
  29. package/dist/web/assets/OccupationsPage-BRdGXQ2P.js +1 -0
  30. package/dist/web/assets/OccupationsPage-BXCJPqBB.css +1 -0
  31. package/dist/web/assets/OverviewPage-C7OJfsL0.css +1 -0
  32. package/dist/web/assets/OverviewPage-x0ExTLqh.js +2 -0
  33. package/dist/web/assets/PersonalProfilePage-Cl2EQuuO.js +1 -0
  34. package/dist/web/assets/PersonalProfilePage-bKUVSOge.css +1 -0
  35. package/dist/web/assets/PersonalProjectsPage-C7k2sBQN.css +1 -0
  36. package/dist/web/assets/PersonalProjectsPage-Dh44vNQk.js +1 -0
  37. package/dist/web/assets/ProjectAgentFirstTask-BVNcBsw9.js +1 -0
  38. package/dist/web/assets/ProjectAgentFirstTask-D1RrWjaX.css +1 -0
  39. package/dist/web/assets/ProjectAgentsPage-BBo_ALw_.js +12 -0
  40. package/dist/web/assets/ProjectAgentsPage-DdooNyYs.css +1 -0
  41. package/dist/web/assets/ProjectScopePicker-CNN_sGrN.js +1 -0
  42. package/dist/web/assets/PublicProjectsPage-BEyOXwu-.js +1 -0
  43. package/dist/web/assets/PublicProjectsPage-DlmcjqGj.css +1 -0
  44. package/dist/web/assets/RoundtablesPage-CdfpgOnb.js +1 -0
  45. package/dist/web/assets/RoundtablesPage-ftnoDxpL.css +1 -0
  46. package/dist/web/assets/SettingsPage-CfyHImb3.css +1 -0
  47. package/dist/web/assets/SettingsPage-DdWrf4wR.js +1 -0
  48. package/dist/web/assets/SharedJefaReportPage-B5vPQ7QK.js +1 -0
  49. package/dist/web/assets/TaskExecutionPlan-BFVcb7KN.css +1 -0
  50. package/dist/web/assets/TaskExecutionPlan-CFM_14VC.js +2 -0
  51. package/dist/web/assets/TaskSchedulesPage-CISJAYZy.js +1 -0
  52. package/dist/web/assets/TaskSchedulesPage-fNCG8EWD.css +1 -0
  53. package/dist/web/assets/TextField-BVqz0DTp.js +1 -0
  54. package/dist/web/assets/UiDialog-CIyDfEYh.js +1 -0
  55. package/dist/web/assets/UiDisclosure-BIE3uXrx.js +1 -0
  56. package/dist/web/assets/UiIcon-EWCwhyKc.css +1 -0
  57. package/dist/web/assets/UiIcon-INPsGFvz.js +1 -0
  58. package/dist/web/assets/UiIconButton-CSFA7F4B.css +1 -0
  59. package/dist/web/assets/UiIconButton-w6pfBSda.js +1 -0
  60. package/dist/web/assets/UiSegmented-wN76K7F9.js +1 -0
  61. package/dist/web/assets/UiSelect-BaVTdhtR.js +1 -0
  62. package/dist/web/assets/UiSelect-CiSPw-7k.css +1 -0
  63. package/dist/web/assets/UiToggle-B2sPCVZe.css +1 -0
  64. package/dist/web/assets/UiToggle-Cs1CZzk6.js +1 -0
  65. package/dist/web/assets/UnassignedConversationsPanel-0y0YliA_.js +1 -0
  66. package/dist/web/assets/VirtualDirectoryList-nAWaJ2eM.js +1 -0
  67. package/dist/web/assets/Workbench3D-BxpszsXO.css +1 -0
  68. package/dist/web/assets/Workbench3D-CYCi_MRp.js +2 -0
  69. package/dist/web/assets/WritingTastePage-B9nMV49S.css +1 -0
  70. package/dist/web/assets/WritingTastePage-DfTuTJx2.js +1 -0
  71. package/dist/web/assets/agent-identity-color-RqpT7gtx.js +1 -0
  72. package/dist/web/assets/agent-occupation-Bke7eg11.js +1 -0
  73. package/dist/web/assets/agent-pins-DrG1uUDt.js +1 -0
  74. package/dist/web/assets/agent-profile-CU4YqRz_.css +1 -0
  75. package/dist/web/assets/agent-work-summary-C53Zfzk5.js +1 -0
  76. package/dist/web/assets/astro-avatar-GADWtfJX.webp +0 -0
  77. package/dist/web/assets/attention-store-Bg-rVsgG.js +1 -0
  78. package/dist/web/assets/{avatars-z6ANYG_r.js → avatars-CUyfvsKO.js} +1 -1
  79. package/dist/web/assets/catalog-CmHt1ONG.js +1 -0
  80. package/dist/web/assets/client-B_QrPkEy.css +1 -0
  81. package/dist/web/assets/client-DBYe0KzE.js +1 -0
  82. package/dist/web/assets/console-2nhA6IaV.js +1 -0
  83. package/dist/web/assets/conversation-launcher-settings-txCBZEle.js +1 -0
  84. package/dist/web/assets/date-time-DBa_h9cD.js +1 -0
  85. package/dist/web/assets/design-lab-ClOnEnF7.js +1 -0
  86. package/dist/web/assets/galaxy-scene-7sJk1pHo.js +1 -0
  87. package/dist/web/assets/i18n-DAeCdA8L.js +10 -0
  88. package/dist/web/assets/{identity-CAQk8V-Q.js → identity-bmKz-BGh.js} +1 -1
  89. package/dist/web/assets/index-DsqCEnEC.css +1 -0
  90. package/dist/web/assets/index-YZYwAHdd.js +2 -0
  91. package/dist/web/assets/judgment-types-C2vHfclm.js +1 -0
  92. package/dist/web/assets/layout-DCeMsVR8.js +1 -0
  93. package/dist/web/assets/model-VWzF8pT3.js +1 -0
  94. package/dist/web/assets/motion-preference-D3Aq7THt.js +1 -0
  95. package/dist/web/assets/occupation-catalog-DnevywIk.js +1 -0
  96. package/dist/web/assets/office-scene-CbmeA3ZZ.js +1 -0
  97. package/dist/web/assets/personal-profile-graph-CyRXX8b0.js +1 -0
  98. package/dist/web/assets/preload-helper-Czpn1I53.js +1 -0
  99. package/dist/web/assets/project-origin-KvE_J110.js +1 -0
  100. package/dist/web/assets/project-team-api-CXPeakuM.js +1 -0
  101. package/dist/web/assets/shared-report-BxeCmoU_.js +1 -0
  102. package/dist/web/assets/shared-report-NXHxLp5x.css +1 -0
  103. package/dist/web/assets/stage-CWdBY1y-.js +1 -0
  104. package/dist/web/assets/stage-D1RyodHo.js +3835 -0
  105. package/dist/web/assets/task-labels-C4t6U38E.js +1 -0
  106. package/dist/web/assets/task-updates-ChvjLQXV.js +1 -0
  107. package/dist/web/assets/ui-WHqcW6KK.js +1 -0
  108. package/dist/web/assets/ui-gkBKztsv.css +1 -0
  109. package/dist/web/assets/useAgentRecords-DotFATN2.js +1 -0
  110. package/dist/web/assets/{useApi-CROJJdhE-sOV3uq1T.js → useApi-CROJJdhE-BJn8l1Cb.js} +1 -1
  111. package/dist/web/assets/vue-router-YsTpOqqM.js +1 -0
  112. package/dist/web/assets/workbench-model-D9ytk327.js +1 -0
  113. package/dist/web/index.html +28 -19
  114. package/docs/agent-conversations-and-collaboration.md +6 -0
  115. package/docs/agent-interface-architecture.md +10 -0
  116. package/docs/agent-roundtable.md +42 -1
  117. package/docs/client-connections.md +158 -110
  118. package/docs/employee-agents.md +7 -3
  119. package/docs/project-agent-memory.md +24 -8
  120. package/graph-provider/fuli_graph/agent_confirmation_progress.py +58 -0
  121. package/graph-provider/fuli_graph/agent_conversation_models.py +1 -0
  122. package/graph-provider/fuli_graph/agent_conversation_routes.py +20 -0
  123. package/graph-provider/fuli_graph/app.py +8 -1
  124. package/graph-provider/fuli_graph/collaboration_context.py +48 -15
  125. package/graph-provider/fuli_graph/config.py +2 -0
  126. package/graph-provider/fuli_graph/conversation_context.py +74 -41
  127. package/graph-provider/fuli_graph/conversation_triage_models.py +35 -0
  128. package/graph-provider/fuli_graph/default_project_lead.py +3 -3
  129. package/graph-provider/fuli_graph/graph_models.py +26 -3
  130. package/graph-provider/fuli_graph/graph_query.py +36 -15
  131. package/graph-provider/fuli_graph/knowledge_search.py +3 -0
  132. package/graph-provider/fuli_graph/knowledge_weight.py +40 -0
  133. package/graph-provider/fuli_graph/knowledge_weight_models.py +9 -0
  134. package/graph-provider/fuli_graph/migrate_lead_occupation.py +165 -0
  135. package/graph-provider/fuli_graph/model_validation.py +10 -0
  136. package/graph-provider/fuli_graph/models.py +44 -3
  137. package/graph-provider/fuli_graph/occupation_catalog.py +116 -0
  138. package/graph-provider/fuli_graph/occupation_routes.py +25 -0
  139. package/graph-provider/fuli_graph/personal_global_preferences.py +6 -10
  140. package/graph-provider/fuli_graph/preference_conflicts.py +38 -0
  141. package/graph-provider/fuli_graph/project_agent_attention_models.py +7 -0
  142. package/graph-provider/fuli_graph/project_agent_coordination_models.py +57 -5
  143. package/graph-provider/fuli_graph/project_agent_delegations.py +129 -13
  144. package/graph-provider/fuli_graph/project_agent_executor_models.py +9 -18
  145. package/graph-provider/fuli_graph/project_agent_hierarchy.py +98 -0
  146. package/graph-provider/fuli_graph/project_agent_memory_routes.py +13 -1
  147. package/graph-provider/fuli_graph/project_agent_models.py +6 -1
  148. package/graph-provider/fuli_graph/project_agent_occupation_models.py +36 -0
  149. package/graph-provider/fuli_graph/project_agent_recruitment_occupation.py +74 -0
  150. package/graph-provider/fuli_graph/project_agent_recruitment_values.py +10 -1
  151. package/graph-provider/fuli_graph/project_agent_remote_origins.py +24 -11
  152. package/graph-provider/fuli_graph/project_agent_routes.py +40 -1
  153. package/graph-provider/fuli_graph/project_agent_task_models.py +31 -17
  154. package/graph-provider/fuli_graph/project_agent_worker_access.py +229 -0
  155. package/graph-provider/fuli_graph/project_agent_worker_models.py +59 -0
  156. package/graph-provider/fuli_graph/project_agent_worker_routes.py +88 -0
  157. package/graph-provider/fuli_graph/project_brief.py +46 -0
  158. package/graph-provider/fuli_graph/project_memory_usage.py +175 -0
  159. package/graph-provider/fuli_graph/project_memory_usage_projection.py +35 -0
  160. package/graph-provider/fuli_graph/project_memory_usage_routes.py +26 -0
  161. package/graph-provider/fuli_graph/provider_values.py +5 -0
  162. package/graph-provider/fuli_graph/runtime.py +6 -0
  163. package/graph-provider/fuli_graph/search_projection.py +6 -27
  164. package/graph-provider/fuli_graph/staffing_demand.py +74 -0
  165. package/graph-provider/fuli_graph/store.py +10 -0
  166. package/graph-provider/fuli_graph/store_agent_conversations.py +88 -16
  167. package/graph-provider/fuli_graph/store_conversation_triage.py +85 -0
  168. package/graph-provider/fuli_graph/store_knowledge.py +21 -6
  169. package/graph-provider/fuli_graph/store_occupation_catalog.py +142 -0
  170. package/graph-provider/fuli_graph/store_project_agent_attention.py +3 -0
  171. package/graph-provider/fuli_graph/store_project_agent_context.py +5 -2
  172. package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +19 -1
  173. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +1 -1
  174. package/graph-provider/fuli_graph/store_project_agent_executors.py +1 -1
  175. package/graph-provider/fuli_graph/store_project_agent_occupation.py +75 -0
  176. package/graph-provider/fuli_graph/store_project_agent_pages.py +84 -0
  177. package/graph-provider/fuli_graph/store_project_agent_task_activity.py +31 -1
  178. package/graph-provider/fuli_graph/store_project_agent_task_persistence.py +3 -1
  179. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +15 -1
  180. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +80 -137
  181. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +32 -7
  182. package/graph-provider/fuli_graph/store_project_agent_tasks.py +42 -4
  183. package/graph-provider/fuli_graph/store_project_agent_worker_runs.py +355 -0
  184. package/graph-provider/fuli_graph/store_project_agents.py +87 -3
  185. package/graph-provider/fuli_graph/store_projects.py +16 -1
  186. package/graph-provider/fuli_graph/store_recruitment_fulfillment.py +95 -0
  187. package/graph-provider/fuli_graph/store_task_contexts.py +189 -33
  188. package/graph-provider/fuli_graph/store_transcript_recovery.py +54 -0
  189. package/graph-provider/fuli_graph/system_agents.py +34 -3
  190. package/graph-provider/fuli_graph/task_activity_origin.py +70 -0
  191. package/graph-provider/fuli_graph/task_context_adoption.py +8 -5
  192. package/graph-provider/fuli_graph/task_context_auto_close.py +54 -0
  193. package/graph-provider/fuli_graph/task_context_coordination.py +114 -0
  194. package/graph-provider/fuli_graph/task_context_models.py +75 -1
  195. package/graph-provider/fuli_graph/task_context_routes.py +11 -1
  196. package/graph-provider/fuli_graph/task_participant_running.py +15 -0
  197. package/graph-provider/fuli_graph/task_worker_attribution.py +102 -0
  198. package/graph-provider/fuli_graph/worker_run_execution.py +48 -0
  199. package/graph-provider/fuli_graph/workflow_candidates.py +4 -9
  200. package/graph-provider/pyproject.toml +1 -1
  201. package/npm-shrinkwrap.json +85 -2
  202. package/package.json +5 -1
  203. package/skills/capturing-session-knowledge/SKILL.md +13 -355
  204. package/skills/capturing-session-knowledge/references/capture.md +145 -0
  205. package/skills/capturing-session-knowledge/references/coordination.md +70 -0
  206. package/skills/capturing-session-knowledge/references/retrieval.md +153 -0
  207. package/src/adaptive-runtime/graph-services.js +8 -34
  208. package/src/adaptive-runtime/settings.js +1 -8
  209. package/src/agent-peer/chat-coordinator.js +104 -0
  210. package/src/agent-peer/chat-port.js +78 -0
  211. package/src/agent-peer/coordinator-http.js +10 -0
  212. package/src/agent-peer/coordinator-service.js +25 -8
  213. package/src/agent-peer/coordinator-store.js +26 -6
  214. package/src/agent-peer/node-store.js +12 -6
  215. package/src/agent-peer/origin-proof.js +6 -4
  216. package/src/agent-peer/peer-port.js +30 -13
  217. package/src/agent-peer/peer-runtime.js +22 -11
  218. package/src/agent-peer/peer-transport.js +1 -1
  219. package/src/agent-peer/protocol.js +16 -2
  220. package/src/agent-peer/runner.js +18 -9
  221. package/src/agent-roundtable/attention-inbox-store.js +37 -0
  222. package/src/agent-roundtable/attention-inbox.js +48 -0
  223. package/src/agent-roundtable/chat-contract.js +49 -0
  224. package/src/agent-roundtable/chat-notifications.js +53 -0
  225. package/src/agent-roundtable/chat-service.js +132 -0
  226. package/src/agent-roundtable/chat-snapshot.js +52 -0
  227. package/src/agent-roundtable/chat-store.js +108 -0
  228. package/src/agent-roundtable/chat-updates.js +28 -0
  229. package/src/agent-roundtable/chat-wait.js +32 -0
  230. package/src/agent-roundtable/chat-wake.js +53 -0
  231. package/src/agent-roundtable/chat.js +98 -0
  232. package/src/agent-roundtable/discovery.js +12 -0
  233. package/src/agent-roundtable/http.js +30 -0
  234. package/src/agent-roundtable/memory-observation.js +25 -0
  235. package/src/agent-roundtable/project-target.js +43 -0
  236. package/src/agent-roundtable/service.js +183 -35
  237. package/src/agent-roundtable/store.js +102 -7
  238. package/src/agent-roundtable/task-context.js +109 -0
  239. package/src/agent-roundtable/tools.js +19 -5
  240. package/src/agent-roundtable/wake.js +106 -18
  241. package/src/agent-tools/agent-attention-definitions.js +5 -4
  242. package/src/agent-tools/agent-collaboration-definitions.js +6 -2
  243. package/src/agent-tools/checkpoint-definitions.js +30 -0
  244. package/src/agent-tools/employee-definitions.js +5 -4
  245. package/src/agent-tools/graph-definitions.js +51 -47
  246. package/src/agent-tools/graph-handlers.js +15 -6
  247. package/src/agent-tools/interface-catalog.js +18 -4
  248. package/src/agent-tools/interface-definitions.js +12 -3
  249. package/src/agent-tools/knowledge-usage-definitions.js +21 -0
  250. package/src/agent-tools/project-agent-definitions.js +14 -4
  251. package/src/agent-tools/project-agent-memory-definitions.js +2 -2
  252. package/src/agent-tools/schema.js +4 -0
  253. package/src/agent-tools/task-preference-definitions.js +13 -0
  254. package/src/agent-tools/task-run-definitions.js +11 -0
  255. package/src/agent-tools/task-schedule-definitions.js +15 -0
  256. package/src/agents/claude-code/host-notification.js +64 -0
  257. package/src/agents/claude-code/lifecycle-hook.js +36 -56
  258. package/src/agents/claude-code/native-session.js +9 -0
  259. package/src/agents/claude-code/preference-evidence.js +28 -0
  260. package/src/agents/claude-code/wake.js +1 -0
  261. package/src/agents/codex/acp-process.js +244 -0
  262. package/src/agents/codex/acp-session.js +190 -0
  263. package/src/agents/codex/acp-worker.js +54 -0
  264. package/src/agents/codex/lifecycle-hook.js +58 -63
  265. package/src/agents/codex/lifecycle-hooks.js +22 -5
  266. package/src/agents/codex/mcp-context.js +16 -0
  267. package/src/agents/codex/preference-evidence.js +21 -0
  268. package/src/agents/codex/token-usage.js +14 -2
  269. package/src/agents/codex/wake.js +7 -3
  270. package/src/agents/cursor/lifecycle-hook.js +6 -16
  271. package/src/agents/identity-receipt.js +74 -10
  272. package/src/agents/lifecycle-input.js +20 -0
  273. package/src/agents/lifecycle-message.js +4 -0
  274. package/src/agents/native-mcp-context.js +9 -0
  275. package/src/agents/native-session-env.js +10 -0
  276. package/src/agents/preference-evidence.js +21 -0
  277. package/src/agents/system-prompt.js +11 -0
  278. package/src/app/agent-request-context.js +14 -1
  279. package/src/app/application-error.js +4 -1
  280. package/src/backup/graph-census.js +57 -0
  281. package/src/backup/identity-reconciliation.js +15 -39
  282. package/src/backup/runtime-adapters.js +64 -27
  283. package/src/backup/runtime-migration.js +88 -0
  284. package/src/cli/command-registry.js +4 -1
  285. package/src/cli/connect-command.js +1 -1
  286. package/src/cli/graph-data-command.js +25 -4
  287. package/src/cli/graph-migrate-command.js +107 -0
  288. package/src/cli/local-runtime-command.js +1 -4
  289. package/src/cli/local-runtime-control.js +27 -0
  290. package/src/cli/mcp-command.js +33 -1
  291. package/src/cli/native-runtime-command.js +44 -0
  292. package/src/cli/remote-mcp-command.js +7 -0
  293. package/src/cli/setup-command.js +12 -22
  294. package/src/cli/terminal-io.js +17 -0
  295. package/src/cli/uninstall-command.js +17 -15
  296. package/src/cli/update-command.js +12 -20
  297. package/src/cli/update-installation.js +3 -3
  298. package/src/cli/worker-command.js +80 -0
  299. package/src/cli/workspace-connection-command.js +1 -4
  300. package/src/cli.js +11 -10
  301. package/src/conversations/capture-window.js +44 -0
  302. package/src/conversations/entry-rules.js +59 -0
  303. package/src/conversations/fallback-transcript-boundary.js +48 -0
  304. package/src/conversations/human-interaction.js +9 -0
  305. package/src/conversations/preference-evidence.js +88 -0
  306. package/src/conversations/preference-lifecycle.js +32 -0
  307. package/src/conversations/preference-session.js +119 -0
  308. package/src/conversations/preference-snapshot.js +93 -0
  309. package/src/conversations/preference-view.js +31 -0
  310. package/src/conversations/stop-auto-close.js +28 -0
  311. package/src/conversations/sync-transcript.js +21 -8
  312. package/src/conversations/task-context-view.js +123 -36
  313. package/src/conversations/transcript-append.js +38 -0
  314. package/src/conversations/transcript-guard-lock.js +59 -0
  315. package/src/conversations/transcript-guard.js +45 -17
  316. package/src/conversations/transcript-lifecycle.js +60 -0
  317. package/src/conversations/transcript-reader.js +24 -3
  318. package/src/desktop/windows-command-options.js +19 -0
  319. package/src/desktop/windows-controller.js +160 -0
  320. package/src/desktop/windows-install.js +55 -0
  321. package/src/desktop/windows-installation-context.js +49 -0
  322. package/src/desktop/windows-installation-switch.js +140 -0
  323. package/src/desktop/windows-installation.js +246 -0
  324. package/src/desktop/windows-rollback-command.js +24 -0
  325. package/src/desktop/windows-rollback.js +69 -0
  326. package/src/desktop/windows-uninstall-command.js +27 -0
  327. package/src/desktop/windows-uninstall.js +94 -0
  328. package/src/desktop/windows-update-command.js +29 -0
  329. package/src/desktop/windows-update-lock.js +38 -0
  330. package/src/desktop/windows-update-runtime.js +57 -0
  331. package/src/desktop/windows-update.js +92 -0
  332. package/src/employees/catalog/assistant.json +26 -0
  333. package/src/employees/manifest.js +2 -2
  334. package/src/employees/recruitment.js +5 -2
  335. package/src/external-knowledge/conflict-policy.js +1 -5
  336. package/src/external-knowledge/connected-search.js +3 -23
  337. package/src/external-knowledge/connectors/custom.js +1 -12
  338. package/src/external-knowledge/connectors/feishu.js +13 -44
  339. package/src/external-knowledge/connectors/mcp.js +2 -22
  340. package/src/external-knowledge/connectors/notion.js +6 -47
  341. package/src/external-knowledge/connectors/response-values.js +34 -0
  342. package/src/external-knowledge/connectors/retrieval-api.js +2 -32
  343. package/src/external-knowledge/document-mapping.js +1 -7
  344. package/src/external-knowledge/service.js +1 -14
  345. package/src/graphiti/agent-attention.js +35 -2
  346. package/src/graphiti/agent-collaboration.js +69 -21
  347. package/src/graphiti/agent-conversations.js +26 -20
  348. package/src/graphiti/agent-knowledge-workflows.js +124 -52
  349. package/src/graphiti/capture-policy.js +23 -0
  350. package/src/graphiti/checkpoint-human-interaction.js +16 -0
  351. package/src/graphiti/collaboration-preference-projection.js +5 -4
  352. package/src/graphiti/collaboration-preference-workflow.js +66 -29
  353. package/src/graphiti/conversation-triage.js +99 -0
  354. package/src/graphiti/execution-mode.js +27 -0
  355. package/src/graphiti/federated-application.js +62 -203
  356. package/src/graphiti/federated-search.js +186 -0
  357. package/src/graphiti/knowledge-provider-mapping.js +8 -0
  358. package/src/graphiti/knowledge-result-budget.js +188 -0
  359. package/src/graphiti/occupation-catalog.js +7 -0
  360. package/src/graphiti/occupation-staffing.js +21 -0
  361. package/src/graphiti/project-agent-control-plane.js +49 -4
  362. package/src/graphiti/project-agent-mapping.js +31 -30
  363. package/src/graphiti/project-agent-memory.js +1 -1
  364. package/src/graphiti/project-agent-run-control.js +64 -0
  365. package/src/graphiti/project-agent-task-entry.js +45 -19
  366. package/src/graphiti/project-agent-team-context.js +9 -5
  367. package/src/graphiti/project-agent-workflows.js +47 -7
  368. package/src/graphiti/project-brief.js +49 -0
  369. package/src/graphiti/project-memory-usage.js +44 -0
  370. package/src/graphiti/project-profile-mapping.js +1 -0
  371. package/src/graphiti/project-team-hierarchy.js +27 -0
  372. package/src/graphiti/project-team-view.js +14 -1
  373. package/src/graphiti/provider-client.js +165 -11
  374. package/src/graphiti/repository-project-registration.js +9 -2
  375. package/src/graphiti/runtime-config.js +19 -8
  376. package/src/graphiti/setup.js +2 -2
  377. package/src/graphiti/task-activity-origin.js +8 -0
  378. package/src/graphiti/task-agent-assignment.js +49 -0
  379. package/src/graphiti/task-collaboration-attribution.js +25 -0
  380. package/src/graphiti/task-entry-activity-origin.js +9 -0
  381. package/src/graphiti/task-entry-preferences.js +42 -28
  382. package/src/graphiti/task-entry-project-mention.js +113 -0
  383. package/src/graphiti/task-entry-project-scope.js +19 -16
  384. package/src/graphiti/task-experiences.js +53 -0
  385. package/src/graphiti/task-knowledge-recall.js +46 -9
  386. package/src/graphiti/task-knowledge-routing.js +130 -0
  387. package/src/graphiti/task-preference-pages.js +91 -0
  388. package/src/graphiti/task-preference-reader.js +118 -0
  389. package/src/graphiti/task-remember.js +100 -0
  390. package/src/graphiti/user-taste-skill.js +4 -15
  391. package/src/graphiti/worker-descriptor.js +42 -0
  392. package/src/graphiti/worker-plan-host.js +92 -0
  393. package/src/graphiti/worker-run-host.js +276 -0
  394. package/src/graphiti/worker-run-view.js +25 -0
  395. package/src/graphiti/worker-task-context.js +154 -0
  396. package/src/graphiti/workflow-observation.js +3 -13
  397. package/src/graphiti/writing-taste-profile.js +4 -15
  398. package/src/http/api-router.js +11 -0
  399. package/src/http/error-mapping.js +4 -0
  400. package/src/http/graph-api-router.js +60 -0
  401. package/src/http/occupation-api-router.js +19 -0
  402. package/src/http/request-policy.js +1 -1
  403. package/src/http/task-events-api-router.js +29 -2
  404. package/src/http/task-schedules-api-router.js +31 -0
  405. package/src/judgment/conversation.js +12 -0
  406. package/src/judgment/engine.js +15 -9
  407. package/src/judgment/routing.js +1 -0
  408. package/src/judgment/service.js +11 -1
  409. package/src/local-runtime/lifecycle.js +20 -15
  410. package/src/local-runtime/processes.js +19 -0
  411. package/src/local-runtime/provider-readiness.js +41 -0
  412. package/src/mcp/chat-listing.js +37 -0
  413. package/src/mcp/context-resources.js +13 -4
  414. package/src/mcp/create-mcp-server.js +138 -35
  415. package/src/mcp/direct-call.js +3 -5
  416. package/src/mcp/instructions.js +15 -1
  417. package/src/mcp/preference-delivery.js +70 -0
  418. package/src/mcp/process-registry.js +65 -0
  419. package/src/mcp/provider-task-context-registry.js +64 -4
  420. package/src/mcp/runtime.js +15 -3
  421. package/src/mcp/session-id.js +7 -0
  422. package/src/mcp/tool-annotations.js +5 -1
  423. package/src/mcp/tool-profiles.js +5 -19
  424. package/src/mcp/tool-result.js +69 -9
  425. package/src/native-runtime/java-runtime.js +47 -0
  426. package/src/native-runtime/macos-ports.js +41 -0
  427. package/src/native-runtime/neo4j-asset-state.js +101 -0
  428. package/src/native-runtime/neo4j-install.js +84 -0
  429. package/src/native-runtime/neo4j-lifecycle.js +151 -0
  430. package/src/native-runtime/neo4j-store-formats.js +36 -0
  431. package/src/native-runtime/runtime.js +246 -146
  432. package/src/native-runtime/windows-assets.js +70 -0
  433. package/src/native-runtime/windows-job-process.cs +169 -0
  434. package/src/native-runtime/windows-job-process.js +110 -0
  435. package/src/native-runtime/windows-processes.js +289 -0
  436. package/src/notifications/needs-person.js +75 -0
  437. package/src/publication/canonical-json.js +13 -7
  438. package/src/server/worker-plan-runtime.js +49 -0
  439. package/src/server.js +18 -3
  440. package/src/setup/agent-installation-status.js +9 -101
  441. package/src/setup/agents/adapter-contract.js +47 -0
  442. package/src/setup/agents/claude-code/adapter.test.js +34 -0
  443. package/src/setup/agents/claude-code/index.js +35 -0
  444. package/src/setup/agents/claude-desktop/adapter.test.js +34 -0
  445. package/src/setup/agents/claude-desktop/index.js +22 -0
  446. package/src/setup/agents/codex/adapter.test.js +34 -0
  447. package/src/setup/agents/codex/index.js +47 -0
  448. package/src/setup/agents/config-directory.js +7 -0
  449. package/src/setup/agents/cursor/adapter.test.js +50 -0
  450. package/src/setup/agents/cursor/index.js +35 -0
  451. package/src/setup/agents/gemini/adapter.test.js +32 -0
  452. package/src/setup/agents/gemini/index.js +14 -0
  453. package/src/setup/agents/json-mcp.js +66 -0
  454. package/src/setup/agents/kiro/adapter.test.js +12 -0
  455. package/src/setup/agents/kiro/index.js +14 -0
  456. package/src/setup/agents/opencode/adapter.test.js +39 -0
  457. package/src/setup/agents/opencode/index.js +19 -0
  458. package/src/setup/agents/qwen-code/adapter.test.js +14 -0
  459. package/src/setup/agents/qwen-code/index.js +14 -0
  460. package/src/setup/agents/registration.js +13 -0
  461. package/src/setup/agents/registry.js +15 -0
  462. package/src/setup/agents.js +13 -122
  463. package/src/setup/claude-code-config.js +18 -18
  464. package/src/setup/claude-desktop-config.js +9 -11
  465. package/src/setup/client-connection.js +1 -1
  466. package/src/setup/codex-bootstrap.js +10 -29
  467. package/src/setup/codex-config.js +59 -26
  468. package/src/setup/container-runtime.js +28 -29
  469. package/src/setup/cursor-config.js +10 -11
  470. package/src/setup/graph-runtime.js +18 -48
  471. package/src/setup/install-agent.js +77 -0
  472. package/src/setup/paths.js +8 -1
  473. package/src/setup/setup.js +17 -36
  474. package/src/setup/uninstall.js +13 -1
  475. package/src/storage/json-file.js +7 -1
  476. package/src/system/client-connection-install.js +27 -0
  477. package/src/system/client-connection-status.js +5 -3
  478. package/src/system/main-module.js +16 -0
  479. package/src/system/open-without-following.js +38 -0
  480. package/src/system/platform-path.js +8 -0
  481. package/src/system/runtime-settings.js +12 -7
  482. package/src/system/settings-apply.js +2 -6
  483. package/src/system/settings-guide.js +85 -0
  484. package/src/system/system-service.js +6 -2
  485. package/src/task-schedules/scheduler.js +32 -0
  486. package/src/task-schedules/service.js +174 -0
  487. package/src/task-schedules/store.js +92 -0
  488. package/src/text/single-line.js +4 -0
  489. package/src/validation/values.js +44 -0
  490. package/dist/web/assets/AboutPage-DC3Eu95G.js +0 -1
  491. package/dist/web/assets/AboutPage-c4dQpUiP.css +0 -1
  492. package/dist/web/assets/AgentHand-Bex3jrOr.js +0 -1
  493. package/dist/web/assets/AgentHand-Y83Oo36U.css +0 -1
  494. package/dist/web/assets/AgentName-E3TrVNIR.js +0 -1
  495. package/dist/web/assets/AgentProfilePage-pHzDx457.js +0 -1
  496. package/dist/web/assets/AgentsDirectoryPage-itsP9chX.js +0 -1
  497. package/dist/web/assets/AgentsDirectoryPage-kc3EgeGO.css +0 -1
  498. package/dist/web/assets/ConnectionsPage-D0L5VcbN.js +0 -1
  499. package/dist/web/assets/EmployeeRecruitDialog-CVbqCzHb.css +0 -1
  500. package/dist/web/assets/EmployeeRecruitDialog-DcYqq75q.js +0 -1
  501. package/dist/web/assets/EmployeeWorkbenchPage-BDN6whBV.js +0 -3
  502. package/dist/web/assets/EmployeeWorkbenchPage-BQes4L1f.css +0 -1
  503. package/dist/web/assets/KnowledgeEditDialog-BnFbCSK7.js +0 -1
  504. package/dist/web/assets/KnowledgeEditDialog-hT9znkoe.css +0 -1
  505. package/dist/web/assets/KnowledgeInspector-u0HypEW7.js +0 -1
  506. package/dist/web/assets/KnowledgeOrganizerPage-B3GoSV-E.js +0 -1
  507. package/dist/web/assets/KnowledgeOrganizerPage-ccgscrPd.css +0 -1
  508. package/dist/web/assets/KnowledgePage-DrRnvfrO.js +0 -1
  509. package/dist/web/assets/KnowledgeWorkspace-D4a7FcAb.css +0 -1
  510. package/dist/web/assets/KnowledgeWorkspace-_wWZQw_h.js +0 -4
  511. package/dist/web/assets/NotFoundPage-BcPx7tfG.js +0 -1
  512. package/dist/web/assets/OverviewPage-8LBN8Shi.css +0 -1
  513. package/dist/web/assets/OverviewPage-C2jrJWDc.js +0 -1
  514. package/dist/web/assets/PersonalProfilePage-B0_zIINs.css +0 -1
  515. package/dist/web/assets/PersonalProfilePage-DkOcops6.js +0 -1
  516. package/dist/web/assets/PersonalProjectsPage-uJXnueKw.js +0 -1
  517. package/dist/web/assets/ProjectAgentFirstTask-Bi1ZsTC1.css +0 -1
  518. package/dist/web/assets/ProjectAgentFirstTask-Cwto-bOU.js +0 -1
  519. package/dist/web/assets/ProjectAgentsPage-DDuhJnAH.js +0 -12
  520. package/dist/web/assets/ProjectAgentsPage-VYiEOpFJ.css +0 -1
  521. package/dist/web/assets/ProjectScopePicker-Bc7A6Rdj.js +0 -1
  522. package/dist/web/assets/PublicProjectsPage-DKamXDV9.css +0 -1
  523. package/dist/web/assets/PublicProjectsPage-KvmU4tDo.js +0 -1
  524. package/dist/web/assets/ReviewPage-BfKG8Hgw.js +0 -1
  525. package/dist/web/assets/RoundtablesPage-Drou-B81.css +0 -1
  526. package/dist/web/assets/RoundtablesPage-IXKQq62P.js +0 -1
  527. package/dist/web/assets/SettingsPage-6FZ7_3-X.css +0 -1
  528. package/dist/web/assets/SettingsPage-Clhe1bei.js +0 -1
  529. package/dist/web/assets/SharedJefaReportPage-B55966fF.js +0 -1
  530. package/dist/web/assets/TaskExecutionPlan-BuEIInIV.css +0 -1
  531. package/dist/web/assets/TaskExecutionPlan-ZoOE8Ltc.js +0 -2
  532. package/dist/web/assets/UiButton-DYcQLYXP.js +0 -1
  533. package/dist/web/assets/UiDialog-D70VuNpZ.js +0 -1
  534. package/dist/web/assets/UiDisclosure-CaeBp1mn.js +0 -1
  535. package/dist/web/assets/UiSegmented-DpXRULl-.js +0 -1
  536. package/dist/web/assets/UiSelect-Alpe2iY3.js +0 -1
  537. package/dist/web/assets/UiSelect-Bv6R0yr1.css +0 -1
  538. package/dist/web/assets/WritingTastePage-B_zYD1df.css +0 -1
  539. package/dist/web/assets/WritingTastePage-BwNCyys9.js +0 -1
  540. package/dist/web/assets/agent-pins-DbcPCnxU.js +0 -1
  541. package/dist/web/assets/agent-profile-DTn-2VAG.css +0 -1
  542. package/dist/web/assets/agent-work-summary-DRJy9v48.js +0 -1
  543. package/dist/web/assets/attention-store-AfhjzTC5.js +0 -1
  544. package/dist/web/assets/catalog-ykylXINx.js +0 -1
  545. package/dist/web/assets/client-CIuQ1icy.css +0 -1
  546. package/dist/web/assets/client-mT6kHxUd.js +0 -1
  547. package/dist/web/assets/console-CDOkjZU8.js +0 -1
  548. package/dist/web/assets/conversation-launcher-settings-CJfHOqqX.js +0 -1
  549. package/dist/web/assets/i18n-DUTWmrzf.js +0 -10
  550. package/dist/web/assets/index-DsExj49_.css +0 -1
  551. package/dist/web/assets/index-pO9miMus.js +0 -5
  552. package/dist/web/assets/judgment-types-aERVgHxJ.js +0 -1
  553. package/dist/web/assets/model-DiN6uWZA.js +0 -1
  554. package/dist/web/assets/personal-profile-graph-C6IZoTJK.js +0 -1
  555. package/dist/web/assets/project-team-api-B3dwwoUU.js +0 -1
  556. package/dist/web/assets/shared-report-CGlCpmNd.css +0 -1
  557. package/dist/web/assets/shared-report-n864kqhr.js +0 -1
  558. package/dist/web/assets/useAgentRoster-BF8eZGbq.js +0 -1
  559. package/dist/web/assets/vue-router-CBdnor1K.js +0 -1
  560. /package/dist/web/assets/{ConnectionsPage-CM9C-zEI.css → TextField-CM9C-zEI.css} +0 -0
  561. /package/dist/web/assets/{KnowledgeInspector-pgDELOEf.css → VirtualDirectoryList-pgDELOEf.css} +0 -0
@@ -1,3 +1,8 @@
1
+ import { nonEmptyText, requiredString, uniqueTextArray } from '../../validation/values.js';
2
+ import { onlineUrl, timeoutMilliseconds } from './response-values.js';
3
+
4
+ const HTTPS_ONLY = Object.freeze(['https:']);
5
+
1
6
  const API_ROOTS = Object.freeze({
2
7
  cn: 'https://open.feishu.cn',
3
8
  global: 'https://open.larksuite.com'
@@ -217,7 +222,7 @@ function feishuRequester(fetchImpl, tokenCache) {
217
222
  'Content-Type': 'application/json; charset=utf-8'
218
223
  },
219
224
  ...(body === undefined ? {} : { body: JSON.stringify(body) }),
220
- signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs))
225
+ signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs, 'Feishu timeoutMs'))
221
226
  });
222
227
  const payload = await response.json().catch(() => ({}));
223
228
  if (!response.ok || payload.code !== 0) {
@@ -243,7 +248,7 @@ async function accessToken(context, fetchImpl, tokenCache) {
243
248
  method: 'POST',
244
249
  headers: { 'Content-Type': 'application/json; charset=utf-8' },
245
250
  body: JSON.stringify({ app_id: appId, app_secret: appSecret }),
246
- signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs))
251
+ signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs, 'Feishu timeoutMs'))
247
252
  }
248
253
  );
249
254
  const payload = await response.json().catch(() => ({}));
@@ -298,9 +303,9 @@ async function searchWiki(context, request, source, query, pageToken, limit = 50
298
303
  }
299
304
 
300
305
  function feishuSource(source, { allowEmpty = false } = {}) {
301
- const nodeTokens = idArray(source?.nodeTokens, 'Feishu nodeTokens');
302
- const spaceId = optionalString(source?.spaceId);
303
- const rootNodeToken = optionalString(source?.rootNodeToken);
306
+ const nodeTokens = uniqueTextArray(source?.nodeTokens, 'Feishu nodeTokens');
307
+ const spaceId = nonEmptyText(source?.spaceId);
308
+ const rootNodeToken = nonEmptyText(source?.rootNodeToken);
304
309
  if (!allowEmpty && !nodeTokens.length && !spaceId) {
305
310
  throw new TypeError('Feishu source requires nodeTokens or spaceId');
306
311
  }
@@ -315,7 +320,7 @@ function nodeSummary(node) {
315
320
  id: node.node_token ?? node.node_id,
316
321
  title: node.title ?? node.node_token ?? node.node_id,
317
322
  type: normalizedObjectType(node.obj_type),
318
- url: onlineUrl(node.url),
323
+ url: onlineUrl(node.url, HTTPS_ONLY),
319
324
  updatedAt: epochDateTime(node.obj_edit_time),
320
325
  metadata: {
321
326
  spaceId: node.space_id ?? null,
@@ -326,9 +331,9 @@ function nodeSummary(node) {
326
331
  }
327
332
 
328
333
  function nodeUrl(node, source) {
329
- const direct = onlineUrl(node.url);
334
+ const direct = onlineUrl(node.url, HTTPS_ONLY);
330
335
  if (direct) return direct;
331
- const base = onlineUrl(source?.webBaseUrl);
336
+ const base = onlineUrl(source?.webBaseUrl, HTTPS_ONLY);
332
337
  const token = node.node_token ?? node.node_id;
333
338
  return base && token ? new URL(`/wiki/${encodeURIComponent(token)}`, base).toString() : null;
334
339
  }
@@ -403,14 +408,6 @@ function environmentValue(env, name, label) {
403
408
  return value;
404
409
  }
405
410
 
406
- function idArray(value, label) {
407
- if (value === undefined || value === null) return [];
408
- if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item.trim())) {
409
- throw new TypeError(`${label} must be an array of nonempty strings`);
410
- }
411
- return [...new Set(value.map((item) => item.trim()))];
412
- }
413
-
414
411
  function rawContentInterval(value) {
415
412
  if (value === undefined || value === null) return 210;
416
413
  if (!Number.isInteger(value) || value < 0 || value > 10_000) {
@@ -419,14 +416,6 @@ function rawContentInterval(value) {
419
416
  return value;
420
417
  }
421
418
 
422
- function timeoutMilliseconds(value) {
423
- if (value === undefined || value === null) return 30_000;
424
- if (!Number.isInteger(value) || value < 1_000 || value > 120_000) {
425
- throw new TypeError('Feishu timeoutMs must be between 1000 and 120000');
426
- }
427
- return value;
428
- }
429
-
430
419
  function pageSize(value) {
431
420
  if (value === undefined || value === null) return 50;
432
421
  if (!Number.isInteger(value) || value <= 0) throw new TypeError('Feishu page size is invalid');
@@ -439,23 +428,3 @@ function epochDateTime(value) {
439
428
  ? new Date(seconds * 1_000).toISOString()
440
429
  : null;
441
430
  }
442
-
443
- function onlineUrl(value) {
444
- if (typeof value !== 'string') return null;
445
- try {
446
- const url = new URL(value);
447
- return url.protocol === 'https:' && !url.username && !url.password ? url.toString() : null;
448
- } catch {
449
- return null;
450
- }
451
- }
452
-
453
- function optionalString(value) {
454
- return typeof value === 'string' && value.trim() ? value.trim() : null;
455
- }
456
-
457
- function requiredString(value, label) {
458
- const normalized = optionalString(value);
459
- if (!normalized) throw new TypeError(`${label} is required`);
460
- return normalized;
461
- }
@@ -4,6 +4,8 @@ import {
4
4
  getDefaultEnvironment
5
5
  } from '@modelcontextprotocol/sdk/client/stdio.js';
6
6
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
7
+ import { requiredString } from '../../validation/values.js';
8
+ import { dateTimeOrNull, onlineUrl } from './response-values.js';
7
9
 
8
10
  const LOCAL_CURSOR_PREFIX = 'fuli-mcp-v1:';
9
11
 
@@ -389,23 +391,6 @@ function mcpHttpUrl(value) {
389
391
  return url;
390
392
  }
391
393
 
392
- function onlineUrl(value) {
393
- if (typeof value !== 'string') return null;
394
- try {
395
- const url = new URL(value);
396
- return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password
397
- ? url.toString()
398
- : null;
399
- } catch {
400
- return null;
401
- }
402
- }
403
-
404
- function dateTimeOrNull(value) {
405
- if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) return null;
406
- return new Date(value).toISOString();
407
- }
408
-
409
394
  function plainObject(value) {
410
395
  return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
411
396
  }
@@ -418,11 +403,6 @@ function stringArray(value) {
418
403
  return value;
419
404
  }
420
405
 
421
- function requiredString(value, label) {
422
- if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} is required`);
423
- return value.trim();
424
- }
425
-
426
406
  function assertEnvironmentName(value) {
427
407
  if (typeof value !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(value)) {
428
408
  throw new TypeError('Environment variable name is invalid');
@@ -1,3 +1,6 @@
1
+ import { requiredString, uniqueTextArray } from '../../validation/values.js';
2
+ import { dateTimeOrNull, jsonObject, onlineUrl, timeoutMilliseconds } from './response-values.js';
3
+
1
4
  const NOTION_API_ROOT = 'https://api.notion.com';
2
5
  const NOTION_API_VERSION = '2026-03-11';
3
6
 
@@ -123,7 +126,7 @@ function notionRequester(fetchImpl) {
123
126
  ...(body === undefined ? {} : { 'Content-Type': 'application/json' })
124
127
  },
125
128
  ...(body === undefined ? {} : { body: JSON.stringify(body) }),
126
- signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs))
129
+ signal: AbortSignal.timeout(timeoutMilliseconds(context.config?.timeoutMs, 'Notion timeoutMs'))
127
130
  });
128
131
  const payload = await response.json().catch(() => ({}));
129
132
  if (!response.ok) {
@@ -178,8 +181,8 @@ async function notionPageDocument(context, request, page) {
178
181
  }
179
182
 
180
183
  function notionSources(source, { allowEmpty = false } = {}) {
181
- const pageIds = idArray(source?.pageIds, 'Notion pageIds');
182
- const dataSourceIds = idArray(source?.dataSourceIds, 'Notion dataSourceIds');
184
+ const pageIds = uniqueTextArray(source?.pageIds, 'Notion pageIds');
185
+ const dataSourceIds = uniqueTextArray(source?.dataSourceIds, 'Notion dataSourceIds');
183
186
  const sources = [
184
187
  ...pageIds.map((id) => ({ kind: 'page', id })),
185
188
  ...dataSourceIds.map((id) => ({ kind: 'data_source', id }))
@@ -256,52 +259,8 @@ function credentialFromEnvironment(context, key, label) {
256
259
  return value;
257
260
  }
258
261
 
259
- function idArray(value, label) {
260
- if (value === undefined || value === null) return [];
261
- if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item.trim())) {
262
- throw new TypeError(`${label} must be an array of nonempty strings`);
263
- }
264
- return [...new Set(value.map((item) => item.trim()))];
265
- }
266
-
267
262
  function pageSize(value) {
268
263
  if (value === undefined || value === null) return 100;
269
264
  if (!Number.isInteger(value) || value <= 0) throw new TypeError('Notion page size is invalid');
270
265
  return Math.min(value, 100);
271
266
  }
272
-
273
- function timeoutMilliseconds(value) {
274
- if (value === undefined || value === null) return 30_000;
275
- if (!Number.isInteger(value) || value < 1_000 || value > 120_000) {
276
- throw new TypeError('Notion timeoutMs must be between 1000 and 120000');
277
- }
278
- return value;
279
- }
280
-
281
- function requiredString(value, label) {
282
- if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} is required`);
283
- return value.trim();
284
- }
285
-
286
- function onlineUrl(value) {
287
- if (typeof value !== 'string') return null;
288
- try {
289
- const url = new URL(value);
290
- return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password
291
- ? url.toString()
292
- : null;
293
- } catch {
294
- return null;
295
- }
296
- }
297
-
298
- function dateTimeOrNull(value) {
299
- if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) return null;
300
- return new Date(value).toISOString();
301
- }
302
-
303
- function jsonObject(value) {
304
- return value && typeof value === 'object' && !Array.isArray(value)
305
- ? structuredClone(value)
306
- : {};
307
- }
@@ -0,0 +1,34 @@
1
+ // Shared readers for values that external knowledge sources return or accept.
2
+
3
+ const WEB_PROTOCOLS = Object.freeze(['http:', 'https:']);
4
+
5
+ // A browsable source link without embedded credentials, otherwise null.
6
+ export function onlineUrl(value, protocols = WEB_PROTOCOLS) {
7
+ if (typeof value !== 'string' || !value) return null;
8
+ try {
9
+ const url = new URL(value);
10
+ return protocols.includes(url.protocol) && !url.username && !url.password ? url.toString() : null;
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+
16
+ export function dateTimeOrNull(value) {
17
+ if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) return null;
18
+ return new Date(value).toISOString();
19
+ }
20
+
21
+ // A detached copy of a JSON object, or an empty object for anything else.
22
+ export function jsonObject(value) {
23
+ return value && typeof value === 'object' && !Array.isArray(value)
24
+ ? structuredClone(value)
25
+ : {};
26
+ }
27
+
28
+ export function timeoutMilliseconds(value, label, fallback = 30_000) {
29
+ if (value === undefined || value === null) return fallback;
30
+ if (!Number.isInteger(value) || value < 1_000 || value > 120_000) {
31
+ throw new TypeError(`${label} must be between 1000 and 120000`);
32
+ }
33
+ return value;
34
+ }
@@ -1,4 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { positiveInteger, requiredString } from '../../validation/values.js';
3
+ import { jsonObject, onlineUrl } from './response-values.js';
2
4
 
3
5
  const DEFAULT_SCORE_THRESHOLD = 0.5;
4
6
  const MAX_KNOWLEDGE_BASES = 32;
@@ -172,24 +174,6 @@ function optionalEnvironmentName(value, label) {
172
174
  return value;
173
175
  }
174
176
 
175
- function jsonObject(value) {
176
- return value && typeof value === 'object' && !Array.isArray(value)
177
- ? structuredClone(value)
178
- : {};
179
- }
180
-
181
- function onlineUrl(value) {
182
- if (!value) return null;
183
- try {
184
- const url = new URL(value);
185
- return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password
186
- ? url.toString()
187
- : null;
188
- } catch {
189
- return null;
190
- }
191
- }
192
-
193
177
  function dateTime(value) {
194
178
  return value && !Number.isNaN(Date.parse(value))
195
179
  ? new Date(value).toISOString()
@@ -206,17 +190,3 @@ function firstString(...values) {
206
190
  }
207
191
  return null;
208
192
  }
209
-
210
- function requiredString(value, label, maximum) {
211
- if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} is required`);
212
- const normalized = value.trim();
213
- if (normalized.length > maximum) throw new TypeError(`${label} is too long`);
214
- return normalized;
215
- }
216
-
217
- function positiveInteger(value, label, maximum) {
218
- if (!Number.isInteger(value) || value <= 0 || value > maximum) {
219
- throw new TypeError(`${label} must be a positive integer no greater than ${maximum}`);
220
- }
221
- return value;
222
- }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
 
3
3
  import { detectSensitiveContent } from '../security/sensitive-content.js';
4
+ import { requiredString } from '../validation/values.js';
4
5
 
5
6
  const MAX_CHUNK_LENGTH = 6_000;
6
7
  const MAX_EXCERPT_LENGTH = 2_048;
@@ -115,13 +116,6 @@ function shortHash(value) {
115
116
  return createHash('sha256').update(value).digest('hex').slice(0, 24);
116
117
  }
117
118
 
118
- function requiredString(value, label, maximum) {
119
- if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} is required`);
120
- const normalized = value.trim();
121
- if (normalized.length > maximum) throw new TypeError(`${label} is too long`);
122
- return normalized;
123
- }
124
-
125
119
  function optionalOnlineUrl(value) {
126
120
  if (value === undefined || value === null || value === '') return null;
127
121
  const url = new URL(value);
@@ -10,6 +10,7 @@ import {
10
10
  } from './document-mapping.js';
11
11
  import { safeExternalSourceDiagnostic } from './safe-diagnostic.js';
12
12
  import { ApplicationError } from '../app/application-error.js';
13
+ import { positiveInteger, requiredString } from '../validation/values.js';
13
14
 
14
15
  const MODES = new Set(['hybrid', 'live', 'mirror']);
15
16
  const MAX_TARGETS = 32;
@@ -780,25 +781,11 @@ function normalizedConfigurationKey(key) {
780
781
  return key.toLocaleLowerCase().replace(/[^a-z0-9]/gu, '');
781
782
  }
782
783
 
783
- function requiredString(value, label, maximum) {
784
- if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} is required`);
785
- const normalized = value.trim();
786
- if (normalized.length > maximum) throw new TypeError(`${label} is too long`);
787
- return normalized;
788
- }
789
-
790
784
  function optionalString(value, label, maximum) {
791
785
  if (value === undefined || value === null || value === '') return null;
792
786
  return requiredString(value, label, maximum);
793
787
  }
794
788
 
795
- function positiveInteger(value, label, maximum) {
796
- if (!Number.isInteger(value) || value <= 0 || value > maximum) {
797
- throw new TypeError(`${label} must be a positive integer no greater than ${maximum}`);
798
- }
799
- return value;
800
- }
801
-
802
789
  function isCredentialDocumentError(error) {
803
790
  return error instanceof TypeError &&
804
791
  error.message === 'Connector item contains credentials and cannot be retrieved or stored';
@@ -1,3 +1,6 @@
1
+ import { nativeChoiceInteraction } from '../conversations/human-interaction.js';
2
+ import { nonEmptyText } from '../validation/values.js';
3
+
1
4
  const fields = {
2
5
  personalSpaceId: 'personal_space_id', personalProjectId: 'personal_project_id',
3
6
  agentId: 'agent_id', taskId: 'task_id', requestId: 'request_id',
@@ -18,9 +21,39 @@ export function attentionRecord(value) {
18
21
 
19
22
  export async function listAgentAttention(application, input) {
20
23
  const value = await application.personal.listAgentAttention(attentionInput(input));
21
- return { ...value, items: value.items.map(attentionRecord) };
24
+ const items = value.items.map(attentionRecord);
25
+ return { ...attentionInteraction(items.find(item => item.status === 'open')), ...value, items };
22
26
  }
23
27
 
24
28
  export async function changeAgentAttention(application, operation, input) {
25
- return attentionRecord(await application.personal.changeAgentAttention(operation, attentionInput(input)));
29
+ const record = attentionRecord(await application.personal.changeAgentAttention(operation, attentionInput(input)));
30
+ return { ...attentionInteraction(record), ...record };
31
+ }
32
+
33
+ // The console sends the chosen option and an optional note; the Provider keeps
34
+ // them as one response text, and the requesting Agent receives both.
35
+ export async function respondToAgentAttention(application, { choice, note, ...input }) {
36
+ const answer = { choice: nonEmptyText(choice), note: nonEmptyText(note) };
37
+ if (!answer.choice && !answer.note) throw new TypeError('An attention reply needs a choice or a note');
38
+ const record = await changeAgentAttention(application, 'respond', { ...input,
39
+ response: [answer.choice, answer.note].filter(Boolean).join('\n\n') });
40
+ return { ...record, delivery: await deliverAnswer(application, record, answer) };
41
+ }
42
+
43
+ // The reply is already recorded; a failed delivery leaves it readable through
44
+ // list_agent_attention and is reported, never hidden.
45
+ async function deliverAnswer(application, record, answer) {
46
+ const unavailable = { status: 'unavailable', agentId: record.agentId };
47
+ if (!application.roundtable) return unavailable;
48
+ try { return await application.roundtable.deliverAttentionReply(record, answer); }
49
+ catch { return unavailable; }
50
+ }
51
+
52
+ function attentionInteraction(record) {
53
+ if (record?.status !== 'open') return {};
54
+ return { human_interaction: nativeChoiceInteraction({
55
+ source: { kind: 'agent_attention', id: record.requestId, revision: record.revision,
56
+ personalSpaceId: record.personalSpaceId, personalProjectId: record.personalProjectId },
57
+ title: record.title, question: record.requestedAction, options: record.options
58
+ }) };
26
59
  }
@@ -1,4 +1,6 @@
1
1
  import { verificationPreference } from './verification-preference.js';
2
+ import { executionPlan } from './execution-mode.js';
3
+ import { dispatchTarget, subLeadIds, reportingParent } from './project-team-hierarchy.js';
2
4
  const lower = values => (values ?? []).map(value => String(value).toLowerCase());
3
5
  // Matches Provider staffing: a nonempty assignment list overrides the profile list.
4
6
  const effective = (assignment, profile, field) => lower(assignment[field]?.length ? assignment[field] : profile?.[field]);
@@ -7,6 +9,7 @@ const verificationKinds = new Set(['test_validation', 'verification', 'code_revi
7
9
  function matchCandidates(agents, task, input) {
8
10
  const required = lower(input.requiredCapabilities);
9
11
  const workKind = String(input.workKind).toLowerCase();
12
+ const occupation = input.occupation ? String(input.occupation).toLowerCase() : null;
10
13
  const candidates = [];
11
14
  for (const agent of Array.isArray(agents) ? agents : []) {
12
15
  for (const assignment of agent.assignments ?? []) {
@@ -14,21 +17,27 @@ function matchCandidates(agents, task, input) {
14
17
  const capabilities = effective(assignment, agent.profile, 'capabilities');
15
18
  if (!required.every(value => capabilities.includes(value))) continue;
16
19
  const workMatch = effective(assignment, agent.profile, 'work_kinds').includes(workKind);
17
- if (!workMatch && !required.length) continue;
20
+ // The lead assigns by occupation first; a matching occupation qualifies on its own.
21
+ const role = agent.occupation?.role ? String(agent.occupation.role).toLowerCase() : null;
22
+ const occupationMatch = Boolean(occupation) && role === occupation;
23
+ if (!workMatch && !occupationMatch && !required.length) continue;
18
24
  candidates.push({ agentId: agent.agent_id, name: agent.profile?.display_name || agent.profile?.name,
19
25
  projectId: assignment.personal_project_id, capabilities,
20
26
  allowedClients: agent.profile?.allowed_clients ?? [],
21
27
  clientAvailable: Boolean(agent.profile?.allowed_clients?.includes(input.sourceApplication)),
22
28
  ...(assignment.responsibility ? { responsibility: assignment.responsibility } : {}),
23
29
  requiresLoan: assignment.personal_project_id !== task.personalProjectId,
30
+ ...(role ? { occupation: role } : {}), occupationMatch,
24
31
  workMatch, activeTasks: agent.open_task_count ?? 0 });
25
32
  }
26
33
  }
27
34
  return candidates;
28
35
  }
29
36
 
30
- function nextSteps(candidate, taskContextToken, sourceLeadId, personalProjectId) {
31
- const steps = { ask: { tool: 'message_agent', arguments: { to: candidate.agentId }, advisoryOnly: true,
37
+ function nextSteps(candidate, taskContextToken, sourceLeadId, personalProjectId, hierarchy = null) {
38
+ const target = hierarchy && !candidate.requiresLoan
39
+ ? dispatchTarget(hierarchy.policy, hierarchy.coordinatorAgentId, candidate.agentId) : candidate.agentId;
40
+ const steps = { ask: { tool: 'message_agent', arguments: { to: target }, advisoryOnly: true,
32
41
  guidance: 'A roundtable answer is advice from a read-only session, not executed work.' } };
33
42
  if (candidate.requiresLoan) {
34
43
  steps.borrow = { tool: 'request_agent_loan', arguments: { sourceProjectId: candidate.projectId, specialistId: candidate.agentId },
@@ -36,7 +45,10 @@ function nextSteps(candidate, taskContextToken, sourceLeadId, personalProjectId)
36
45
  guidance: 'Only the destination project lead may request. The source lead approves with decide_agent_loan in its own task context before any assignment or execution.' };
37
46
  }
38
47
  steps.execute = { tool: 'coordinate_project_agent_task',
39
- arguments: { taskContextToken, personalProjectId, collaboratorAgentIds: [candidate.agentId] },
48
+ arguments: { taskContextToken, personalProjectId, ...(hierarchy ? {
49
+ coordinatorAgentId: hierarchy.coordinatorAgentId, leadAgentId: target,
50
+ ...(target !== candidate.agentId ? { workKind: 'delegation', requiredCapabilities: [] } : {})
51
+ } : { collaboratorAgentIds: [candidate.agentId] }) },
40
52
  guidance: `${candidate.requiresLoan ? 'After the loan is approved, ' : ''}coordinate under the project lead with an authorized runtime; record the actual worker and result back to the lead.` };
41
53
  return steps;
42
54
  }
@@ -59,26 +71,49 @@ export async function planAgentCollaboration(application, input) {
59
71
  const separated = input.taskSize !== 'small' && !verificationOnly;
60
72
  const independent = separated && input.taskSize === 'complex' ? input.independentWork : null;
61
73
  const verification = { workKind: input.verificationWorkKind ?? 'test_validation',
74
+ occupation: input.verificationOccupation ?? 'testing',
62
75
  requiredCapabilities: input.verificationCapabilities ?? [] };
63
76
  const pools = { main: matchCandidates(agents, task, input),
64
77
  independent: independent ? matchCandidates(agents, task, { ...input, ...independent,
65
- requiredCapabilities: independent.requiredCapabilities ?? [] }) : [],
78
+ requiredCapabilities: independent.requiredCapabilities ?? [], occupation: independent.occupation ?? null }) : [],
66
79
  verification: separated ? matchCandidates(agents, task, { ...input, ...verification }) : [] };
67
80
  // A loan needs the source project's lead, who itself cannot be borrowed.
68
81
  const sourceProjects = [...new Set(Object.values(pools).flat().filter(item => item.requiresLoan).map(item => item.projectId))];
69
82
  const sourceLeads = Object.fromEntries(await Promise.all(sourceProjects.map(async projectId => [projectId,
70
83
  (await application.personal.getProjectAgentCoordinationPolicy(spaceId, projectId))?.team_lead_agent_id ?? null])));
71
84
  const token = input.taskContextToken;
72
- const eligible = (pool, excluded = [], available = true) => pool.filter(item => item.clientAvailable === available && !excluded.includes(item.agentId)
85
+ const subLeads = subLeadIds(policy);
86
+ const coordinatorAgentId = subLeads.has(task.projectAgentId) ? task.projectAgentId
87
+ : policy.team_lead_agent_id ?? task.projectAgentId;
88
+ const memberOwner = Boolean(policy.sub_teams?.length && task.projectAgentId !== policy.team_lead_agent_id
89
+ && !subLeads.has(task.projectAgentId));
90
+ const hierarchy = policy.sub_teams?.length ? { policy, coordinatorAgentId } : null;
91
+ const candidateSteps = candidate => nextSteps(candidate, token, sourceLeads[candidate.projectId], task.personalProjectId, hierarchy);
92
+ const dispatchable = candidate => !policy.sub_teams?.length || (memberOwner ? candidate.agentId === task.projectAgentId : candidate.requiresLoan
93
+ ? coordinatorAgentId === policy.team_lead_agent_id
94
+ : dispatchTarget(policy, coordinatorAgentId, candidate.agentId) !== null);
95
+ const throughLead = item => {
96
+ const target = hierarchy && !item.requiresLoan ? dispatchTarget(policy, coordinatorAgentId, item.agentId) : item.agentId;
97
+ if (target === item.agentId || !target) return item;
98
+ const lead = agents.find(agent => agent.agent_id === target && agent.profile?.status !== 'archived'
99
+ && agent.assignments?.some(assignment => assignment.status === 'active' && assignment.personal_project_id === task.personalProjectId));
100
+ return { ...item, clientAvailable: item.clientAvailable && Boolean(lead?.profile?.allowed_clients?.includes(input.sourceApplication)),
101
+ dispatchAgentId: target };
102
+ };
103
+ const eligible = (pool, excluded = [], available = true) => pool.map(throughLead).filter(item => item.clientAvailable === available && !excluded.includes(item.agentId)
104
+ && dispatchable(item)
105
+ && !(subLeads.has(item.agentId) && item.agentId === coordinatorAgentId
106
+ && !['planning', 'delegation', 'integration', 'reporting'].includes(input.workKind))
73
107
  && (!item.requiresLoan || (sourceLeads[item.projectId] && sourceLeads[item.projectId] !== item.agentId)))
74
- .sort((a, b) => Number(a.requiresLoan) - Number(b.requiresLoan) || Number(b.workMatch) - Number(a.workMatch)
108
+ .sort((a, b) => Number(b.occupationMatch) - Number(a.occupationMatch)
109
+ || Number(a.requiresLoan) - Number(b.requiresLoan) || Number(b.workMatch) - Number(a.workMatch)
75
110
  || a.activeTasks - b.activeTasks || a.agentId.localeCompare(b.agentId));
76
111
  const authors = [...new Set([task.projectAgentId, ...(input.implementationAgentIds ?? [])])];
77
112
  const mainCandidates = eligible(pools.main, independentAcceptance ? authors : []);
78
113
  const unavailableCandidates = eligible(pools.main, independentAcceptance ? authors : [], false);
79
114
  const shown = mainCandidates.slice(0, 8).map(item => ({ ...item,
80
115
  ...(item.requiresLoan ? { sourceLeadId: sourceLeads[item.projectId] } : {}),
81
- nextSteps: nextSteps(item, token, sourceLeads[item.projectId], task.personalProjectId) }));
116
+ nextSteps: candidateSteps(item) }));
82
117
  const preferencePrompt = verificationPreference({ executors, rules, projectId: task.personalProjectId,
83
118
  lockedExecutorIds: currentAgent.profile?.executorPolicy?.mode === 'locked' ? currentAgent.profile.executorPolicy.lockedExecutorIds : null });
84
119
  const judgmentPolicy = await application.judgment?.policy({ personalSpaceId: spaceId, personalProjectId: task.personalProjectId });
@@ -91,46 +126,59 @@ export async function planAgentCollaboration(application, input) {
91
126
  const recruit = stream => ({ tool: 'coordinate_project_agent_task',
92
127
  arguments: { taskContextToken: token, personalProjectId: task.personalProjectId,
93
128
  workKind: stream.workKind, requiredCapabilities: stream.requiredCapabilities ?? [],
94
- staffingIntent: 'new_durable' },
129
+ staffingIntent: 'new_durable',
130
+ // Bole hires by the occupation the workstream asked for, not by guessing from its work kind.
131
+ ...(stream.occupation ? { recruitmentOccupation: { role: stream.occupation, tags: [] } } : {}) },
95
132
  requires: ['idempotencyKey', 'title', 'objective', 'routingReason', 'contextQueries'],
96
133
  coordinatorAgentId: policy.team_lead_agent_id ?? null, askBeforeRecruitment: policy.ask_before_recruitment !== false,
97
134
  guidance: 'Reuse one stable idempotencyKey for this staffing need so a retry reuses the same recruitment. When askBeforeRecruitment is true, AR only proposes a hire; confirm with the user before it is staffed. Recruiting executes nothing.' });
98
- const recruitment = shown.length || unavailableCandidates.length ? null : recruit(input);
99
- const coordinatorAgentId = policy.team_lead_agent_id ?? task.projectAgentId;
135
+ const subTeamCoordinator = subLeads.has(coordinatorAgentId) || memberOwner;
136
+ const recruitment = shown.length || unavailableCandidates.length || subTeamCoordinator ? null : recruit(input);
100
137
  function workstream(id, spec, candidate, dependsOn = [], unavailable = []) {
101
- const continuing = id === 'main' && !independentAcceptance && candidate?.agentId === task.projectAgentId;
102
- const blocked = !candidate && unavailable.length > 0;
138
+ const target = candidate && !candidate.requiresLoan ? dispatchTarget(policy, coordinatorAgentId, candidate.agentId) : candidate?.agentId;
139
+ const viaSubLead = Boolean(candidate && target !== coordinatorAgentId && subLeads.has(target));
140
+ const continuing = id === 'main' && !independentAcceptance && target === task.projectAgentId;
141
+ const scopeGap = !candidate && !unavailable.length && subTeamCoordinator;
142
+ const blocked = !candidate && (unavailable.length > 0 || scopeGap);
103
143
  const dispatch = continuing || blocked ? null : candidate ? { tool: 'coordinate_project_agent_task',
104
144
  arguments: { taskContextToken: token, workKind: spec.workKind, requiredCapabilities: spec.requiredCapabilities ?? [],
105
- leadAgentId: candidate.agentId },
145
+ leadAgentId: target, ...(viaSubLead ? { workKind: 'delegation', requiredCapabilities: [] } : {}) },
106
146
  requires: ['idempotencyKey', 'title', 'objective', 'contextQueries'] } : recruit(spec);
107
147
  if (dispatch) Object.assign(dispatch.arguments, { personalProjectId: task.personalProjectId, coordinatorAgentId, contextLimitPerQuery: 3,
108
148
  routingReason: `Bounded ${id} workstream; ${dependsOn.length ? 'independent acceptance after stable artifacts' : 'explicit scope'}; report evidence to the project lead.`,
109
149
  ...(spec.contextQueries ? { contextQueries: spec.contextQueries } : {}) });
110
- return { id, workKind: spec.workKind, agentId: candidate?.agentId ?? null,
150
+ return { id, workKind: spec.workKind, agentId: target ?? null,
151
+ ...(viaSubLead ? { executionCandidateAgentId: candidate.agentId,
152
+ reportingGuidance: 'Send a bounded coordination brief to the sub-lead; it delegates within its group and reports to the project lead.' } : {}),
111
153
  boundary: spec.boundary ?? (verificationOnly || id === 'verification'
112
154
  ? '独立检查约定产物与验收条件,返回问题、命令和结果;不改动主线实现。'
113
155
  : '负责主线实施与集成;独立工作包之外的修改由此处统一处理。'),
114
156
  action: continuing ? 'continue_current' : blocked ? 'blocked' : candidate?.requiresLoan ? 'request_loan' : candidate ? 'delegate' : 'recruit',
115
157
  dependsOn, startWhen: dependsOn.length ? 'artifacts_ready' : 'now', dispatch,
116
- ...(blocked ? { blockedReason: 'qualified_agent_client_unavailable', unavailableCandidates: unavailable,
158
+ ...(scopeGap ? { blockedReason: 'sub_team_scope_gap', reportToAgentId: reportingParent(policy, task.projectAgentId),
159
+ guidance: 'Report this staffing gap to the project lead; do not bypass your group or recruit a replacement.' }
160
+ : blocked ? { blockedReason: 'qualified_agent_client_unavailable', unavailableCandidates: unavailable,
117
161
  guidance: 'A qualified FLA exists but cannot use this client. Select an already authorized eligible client with assess_agent_action or report the constraint; do not recruit a replacement or relax its allowed clients.' } : {}),
118
162
  ...(candidate?.requiresLoan ? { borrow: nextSteps(candidate, token, sourceLeads[candidate.projectId], task.personalProjectId).borrow } : {}) };
119
163
  }
120
- const main = !independentAcceptance && mainCandidates.find(candidate => candidate.agentId === task.projectAgentId)
121
- || mainCandidates[0];
164
+ // The lead keeps the main work only when no other occupation was asked for.
165
+ // An explicitly requested occupation is a requirement: without such a member, Bole recruits one.
166
+ const pick = (list, occupation) => occupation ? list.find(candidate => candidate.occupationMatch) : list[0];
167
+ const main = !independentAcceptance && mainCandidates.find(candidate => candidate.agentId === task.projectAgentId
168
+ && (!input.occupation || candidate.occupationMatch)) || pick(mainCandidates, input.occupation);
122
169
  const workstreams = [workstream('main', input, main, [], unavailableCandidates)];
123
170
  if (independent) {
124
171
  const excluded = [...authors, main?.agentId];
125
- workstreams.push(workstream('independent', independent, eligible(pools.independent, excluded)[0], [],
172
+ workstreams.push(workstream('independent', independent, pick(eligible(pools.independent, excluded), independent.occupation), [],
126
173
  eligible(pools.independent, excluded, false)));
127
174
  }
128
175
  if (separated) {
129
176
  const excluded = [...authors, ...workstreams.map(stream => stream.agentId)];
130
- workstreams.push(workstream('verification', verification, eligible(pools.verification, excluded)[0],
177
+ workstreams.push(workstream('verification', verification, pick(eligible(pools.verification, excluded), input.verificationOccupation),
131
178
  workstreams.map(stream => stream.id), eligible(pools.verification, excluded, false)));
132
179
  }
133
180
  const divisionPlan = { mode: separated ? 'separated' : 'single', coordinatorAgentId, workstreams,
181
+ execution: executionPlan(workstreams, input.hostExecutionModes, token),
134
182
  reason: separated ? (independent ? '复杂任务有明确独立边界,拆为两个执行工作包和一份独立验收。'
135
183
  : '主线延续当前上下文,另一个 FLA 在产物稳定后独立验收,减少重复阅读。')
136
184
  : independentAcceptance ? '这是独立验收工作包,排除已知实现者,不再递归拆分。'
@@ -144,7 +192,7 @@ export async function planAgentCollaboration(application, input) {
144
192
  candidates: shown, unavailableCandidates, recruitment, pendingLoans: loans.loans?.filter(loan => loan.status === 'requested') ?? [],
145
193
  executionStarted: false,
146
194
  preferencePrompt, divisionPlan,
147
- guidance: 'Keep the project lead as owner. Choose a qualified specialist. Discovery and roundtable answers are advisory and are not executed work. For another project, the destination lead calls request_agent_loan; the source lead approves with decide_agent_loan in its own scoped task. Then coordinate_project_agent_task with the borrowed Agent. Share only the bounded task brief and authorized artifacts. Workers report results to the project lead; record actual workers. Verify each artifact; use record_agent_verification. Two failed attempts at one capability tier require escalation, not another identical retry. A selected model is not proof of execution or success.' };
195
+ guidance: 'Keep the project lead as user-facing owner. Choose a qualified specialist through its immediate reporting lead; each lead integrates reports upward. Discovery and roundtable answers are advisory and are not executed work. For another project, the destination lead calls request_agent_loan; the source lead approves with decide_agent_loan in its own scoped task. Then coordinate_project_agent_task with the borrowed Agent. Share only the bounded task brief and authorized artifacts; record actual workers. Verify each artifact; use record_agent_verification. Two failed attempts at one capability tier require escalation, not another identical retry. A selected model is not proof of execution or success.' };
148
196
  }
149
197
 
150
198
  export async function agentLoan(application, operation, input) {