fuli-context 0.7.9 → 0.7.10-beta.7

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 (150) hide show
  1. package/README.md +5 -2
  2. package/README.zh-CN.md +5 -3
  3. package/dist/web/assets/{AboutPage-Bp-Xz6yo.js → AboutPage-EtWPOSpz.js} +1 -1
  4. package/dist/web/assets/AboutPage-vYIBifKw.css +1 -0
  5. package/dist/web/assets/AgentHand-D5pOgVjJ.js +1 -0
  6. package/dist/web/assets/AgentHand-DvdNtJ3_.css +1 -0
  7. package/dist/web/assets/ConnectionsPage-DXkvMuyK.js +1 -0
  8. package/dist/web/assets/EmployeeRecruitDialog-B59X3rOi.css +1 -0
  9. package/dist/web/assets/EmployeeRecruitDialog-DONxXnd2.js +1 -0
  10. package/dist/web/assets/EmployeeWorkbenchPage-Cd4hd4bf.js +3 -0
  11. package/dist/web/assets/EmployeeWorkbenchPage-Cz7y-IFD.css +1 -0
  12. package/dist/web/assets/GrowthLoading-B_Hh0CRS.css +1 -0
  13. package/dist/web/assets/GrowthLoading-CCYPgE2f.js +1 -0
  14. package/dist/web/assets/KnowledgeEditDialog-BUqMfa0a.js +1 -0
  15. package/dist/web/assets/KnowledgeEditDialog-D9NuRHvp.css +1 -0
  16. package/dist/web/assets/KnowledgeInspector-DlVPDGP3.js +1 -0
  17. package/dist/web/assets/KnowledgeOrganizerPage-DhthZGKZ.js +1 -0
  18. package/dist/web/assets/KnowledgeOrganizerPage-h2iKvQpO.css +1 -0
  19. package/dist/web/assets/KnowledgePage-B6RDVxq-.js +1 -0
  20. package/dist/web/assets/KnowledgeWorkspace-mVFHQcxr.js +4 -0
  21. package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +1 -0
  22. package/dist/web/assets/NotFoundPage-CCeEdBy9.js +1 -0
  23. package/dist/web/assets/{OverviewPage-HwoSW-5z.js → OverviewPage-CnYWoZ3r.js} +1 -1
  24. package/dist/web/assets/PersonalProfilePage-Bz4zpTPC.js +1 -0
  25. package/dist/web/assets/PersonalProfilePage-yQRCBude.css +1 -0
  26. package/dist/web/assets/PersonalProjectsPage-2u1o6dvA.js +1 -0
  27. package/dist/web/assets/ProjectAgentsPage-8tci631q.js +11 -0
  28. package/dist/web/assets/ProjectAgentsPage-BN4k3ubu.css +1 -0
  29. package/dist/web/assets/ProjectScopePicker-BcPtIcv8.js +1 -0
  30. package/dist/web/assets/ProjectScopePicker-BvaGTMFx.css +1 -0
  31. package/dist/web/assets/PublicProjectsPage-BmBHfnud.js +1 -0
  32. package/dist/web/assets/ReviewPage-tvt2eqhw.js +1 -0
  33. package/dist/web/assets/{SearchableSelect-CFZNdtlY.js → SearchableSelect-BjBDBVuZ.js} +1 -1
  34. package/dist/web/assets/SettingsPage-Brl-SL33.css +1 -0
  35. package/dist/web/assets/SettingsPage-D9mNzBg-.js +1 -0
  36. package/dist/web/assets/SharedJefaReportPage-DXbj_JhO.css +1 -0
  37. package/dist/web/assets/SharedJefaReportPage-Dn_HBgaa.js +1 -0
  38. package/dist/web/assets/WritingTastePage-BCwimXC-.js +1 -0
  39. package/dist/web/assets/WritingTastePage-tb_pgude.css +1 -0
  40. package/dist/web/assets/bole-avatar-CVIr-bU_.webp +0 -0
  41. package/dist/web/assets/client-v8XKgQji.js +1 -0
  42. package/dist/web/assets/console-C9HS-zbd.js +1 -0
  43. package/dist/web/assets/conversation-launcher-settings-DMDCpf_N.js +1 -0
  44. package/dist/web/assets/employee-dialog-Bh5ccIbK.css +1 -0
  45. package/dist/web/assets/{i18n-DQH9D35M.js → i18n-BkJjUsz_.js} +3 -3
  46. package/dist/web/assets/{identity-JjZG6aS5.js → identity-74yGiMzO.js} +1 -1
  47. package/dist/web/assets/index-CwjxeHGk.css +1 -0
  48. package/dist/web/assets/index-Dioqmmvf.js +2 -0
  49. package/dist/web/assets/{model-zL97NJ-U.js → model-86tZgY0r.js} +1 -1
  50. package/dist/web/assets/shared-report-CLg82y4D.js +1 -0
  51. package/dist/web/assets/shared-report-ezeb8dW4.css +1 -0
  52. package/dist/web/assets/{useApi-CROJJdhE-CElDxEZI.js → useApi-CROJJdhE-BBUO_YEu.js} +1 -1
  53. package/dist/web/assets/useModalDialog-DrLhqtjR.js +1 -0
  54. package/dist/web/assets/{vue-router-F7MnkVlW.js → vue-router-Cw4QZxM1.js} +1 -1
  55. package/dist/web/index.html +14 -8
  56. package/docs/agent-interface-architecture.md +97 -0
  57. package/docs/employee-agents.md +58 -2
  58. package/graph-provider/fuli_graph/collaboration_context.py +2 -1
  59. package/graph-provider/fuli_graph/graph_models.py +5 -3
  60. package/graph-provider/fuli_graph/graph_query.py +4 -0
  61. package/graph-provider/fuli_graph/knowledge_search.py +2 -1
  62. package/graph-provider/fuli_graph/project_agent_access.py +2 -0
  63. package/graph-provider/fuli_graph/project_agent_attention_models.py +64 -0
  64. package/graph-provider/fuli_graph/project_agent_attention_routes.py +38 -0
  65. package/graph-provider/fuli_graph/project_agent_models.py +2 -0
  66. package/graph-provider/fuli_graph/project_agent_names.py +33 -0
  67. package/graph-provider/fuli_graph/project_agent_routes.py +12 -0
  68. package/graph-provider/fuli_graph/runtime.py +49 -20
  69. package/graph-provider/fuli_graph/store.py +2 -0
  70. package/graph-provider/fuli_graph/store_knowledge.py +4 -2
  71. package/graph-provider/fuli_graph/store_project_agent_attention.py +138 -0
  72. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +8 -0
  73. package/graph-provider/fuli_graph/store_project_agent_memory.py +40 -13
  74. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +4 -1
  75. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +4 -1
  76. package/graph-provider/fuli_graph/store_project_agent_tasks.py +3 -0
  77. package/graph-provider/fuli_graph/store_project_agents.py +109 -0
  78. package/graph-provider/fuli_graph/store_task_contexts.py +1 -1
  79. package/graph-provider/fuli_graph/system_hr_identity.py +218 -0
  80. package/graph-provider/pyproject.toml +1 -1
  81. package/npm-shrinkwrap.json +52 -60
  82. package/package.json +2 -2
  83. package/skills/operating-fuli/SKILL.md +47 -0
  84. package/skills/operating-fuli/agents/openai.yaml +4 -0
  85. package/skills/operating-fuli/references/workflow-recipes.md +33 -0
  86. package/src/agent-tools/agent-attention-definitions.js +33 -0
  87. package/src/agent-tools/graph-definitions.js +8 -2
  88. package/src/agent-tools/graph-handlers.js +41 -0
  89. package/src/agent-tools/interface-catalog.js +101 -0
  90. package/src/agent-tools/interface-definitions.js +184 -0
  91. package/src/agent-tools/project-agent-definitions.js +1 -0
  92. package/src/cli/local-runtime-command.js +2 -1
  93. package/src/employees/catalog/bole.json +19 -0
  94. package/src/employees/install-package.js +3 -0
  95. package/src/employees/manifest.js +4 -0
  96. package/src/employees/package-registry.js +2 -0
  97. package/src/employees/recruitment.js +3 -0
  98. package/src/employees/service.js +10 -0
  99. package/src/external-knowledge/registry.js +24 -0
  100. package/src/external-knowledge/service.js +34 -1
  101. package/src/graphiti/agent-attention.js +26 -0
  102. package/src/graphiti/agent-knowledge-workflows.js +1 -0
  103. package/src/graphiti/project-agent-control-plane.js +21 -0
  104. package/src/graphiti/project-agent-mapping.js +6 -0
  105. package/src/graphiti/provider-client.js +13 -0
  106. package/src/graphiti/workspace-provider-client.js +9 -1
  107. package/src/http/agent-attention-api-router.js +28 -0
  108. package/src/http/error-mapping.js +3 -0
  109. package/src/http/graph-api-router.js +2 -0
  110. package/src/local-runtime/lifecycle.js +8 -2
  111. package/src/mcp/create-mcp-server.js +10 -1
  112. package/src/mcp/tool-annotations.js +31 -0
  113. package/src/mcp/tool-result.js +4 -4
  114. package/src/setup/agent-installation-status.js +2 -1
  115. package/src/setup/agents.js +4 -0
  116. package/src/setup/graph-runtime.js +4 -0
  117. package/src/setup/paths.js +2 -1
  118. package/src/setup/setup.js +4 -2
  119. package/src/setup/uninstall.js +4 -2
  120. package/src/system/resource-monitor.js +11 -1
  121. package/src/system/runtime-settings.js +7 -0
  122. package/dist/web/assets/AboutPage-DXh7Fwxt.css +0 -1
  123. package/dist/web/assets/ConnectionsPage-BMpkYeSE.js +0 -1
  124. package/dist/web/assets/EmployeeRecruitDialog-CnA7rOnB.css +0 -1
  125. package/dist/web/assets/EmployeeRecruitDialog-DbMVuLAI.js +0 -1
  126. package/dist/web/assets/EmployeeWorkbenchPage-BpkqpEs7.css +0 -1
  127. package/dist/web/assets/EmployeeWorkbenchPage-DtXSJOpq.js +0 -1
  128. package/dist/web/assets/KnowledgeEditDialog-DADXj_BT.js +0 -1
  129. package/dist/web/assets/KnowledgeEditDialog-iv-dgUeD.css +0 -1
  130. package/dist/web/assets/KnowledgeInspector-Xs4VCUg8.js +0 -1
  131. package/dist/web/assets/KnowledgeOrganizerPage-B_Ct4bB2.js +0 -1
  132. package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +0 -1
  133. package/dist/web/assets/KnowledgePage-RG9Ttnrp.js +0 -1
  134. package/dist/web/assets/KnowledgeWorkspace-DI8YqTVI.js +0 -4
  135. package/dist/web/assets/PersonalProfilePage-B7T09bow.css +0 -1
  136. package/dist/web/assets/PersonalProfilePage-DCoG52SW.js +0 -1
  137. package/dist/web/assets/PersonalProjectsPage-CNb9mlbp.js +0 -1
  138. package/dist/web/assets/ProjectAgentsPage-C3vW6wmk.css +0 -1
  139. package/dist/web/assets/ProjectAgentsPage-zQ9BZW9v.js +0 -11
  140. package/dist/web/assets/PublicProjectsPage-BuLqkZ8g.js +0 -1
  141. package/dist/web/assets/ReviewPage-8A7N5pJs.js +0 -1
  142. package/dist/web/assets/SettingsPage-4ah1u906.js +0 -1
  143. package/dist/web/assets/SettingsPage-CwZp2ka4.css +0 -1
  144. package/dist/web/assets/WritingTastePage-DSgvTH6x.css +0 -1
  145. package/dist/web/assets/WritingTastePage-DztRj6MK.js +0 -1
  146. package/dist/web/assets/avatars-LvVKbDF7.js +0 -1
  147. package/dist/web/assets/console-B7dWYAoU.js +0 -1
  148. package/dist/web/assets/conversation-launcher-settings-D1xhW1S5.js +0 -1
  149. package/dist/web/assets/index-Ch1jmkyO.css +0 -1
  150. package/dist/web/assets/index-DRd-2Tii.js +0 -2
@@ -1,6 +1,37 @@
1
1
  import { ApplicationError } from '../app/application-error.js';
2
+ import { agentInterfaceCatalog } from './interface-catalog.js';
2
3
 
3
4
  const HANDLERS = Object.freeze({
5
+ list_project_agent_tasks: (app, input) => app.listProjectAgentTasks(input),
6
+ request_agent_attention: (app, input) => app.requestAgentAttention(input),
7
+ list_agent_attention: (app, input) => app.listAgentAttention(input),
8
+ cancel_agent_attention: (app, input) => app.cancelAgentAttention(input),
9
+ list_preference_conflicts: (app, input) => app.listPreferenceConflicts(input),
10
+ list_agent_interfaces: () => agentInterfaceCatalog(),
11
+ get_capture_policy: (app) => app.getCapturePolicy(),
12
+ update_capture_policy: (app, input) => app.updateCapturePolicy(input),
13
+ list_external_knowledge_connectors: (app) => externalKnowledge(app).listConnectorTypes(),
14
+ discover_external_knowledge_sources: (app, input) => externalKnowledge(app).discover(input),
15
+ list_external_knowledge_bindings: (app) => externalKnowledge(app).listBindings(),
16
+ create_external_knowledge_binding: (app, input) => externalKnowledge(app).createBinding(input),
17
+ check_external_knowledge_binding: (app, input) => externalKnowledge(app).checkBinding(input.bindingId),
18
+ sync_external_knowledge_binding: (app, { bindingId, ...input }) =>
19
+ externalKnowledge(app).syncBinding(bindingId, input),
20
+ retrieve_external_knowledge_binding: (app, { bindingId, ...input }) =>
21
+ externalKnowledge(app).retrieveBinding(bindingId, input),
22
+ update_external_knowledge_binding_targets: (app, { bindingId, ...input }) =>
23
+ externalKnowledge(app).updateBindingTargets(bindingId, input),
24
+ delete_external_knowledge_binding: (app, input) =>
25
+ externalKnowledge(app).deleteBinding(input.bindingId),
26
+ get_external_knowledge_conflict_policy: (app, input) =>
27
+ connectedKnowledge(app).getConflictPolicy(input),
28
+ update_external_knowledge_conflict_policy: (app, input) =>
29
+ connectedKnowledge(app).updateConflictPolicy(input),
30
+ defer_preference_conflict: (app, input) => app.deferPreferenceConflict({
31
+ ...input,
32
+ operationActor: 'agent'
33
+ }),
34
+ delete_public_project: (app, input) => app.deletePublicProject(input),
4
35
  list_employee_templates: (app, input) => employeeService(app).list(input),
5
36
  recruit_employee: (app, input) => employeeService(app).recruit(input),
6
37
  list_employee_tools: (app, input) => employeeService(app).describeTools(input),
@@ -182,6 +213,16 @@ function employeeService(app) {
182
213
  return app.employees;
183
214
  }
184
215
 
216
+ function externalKnowledge(app) {
217
+ if (!app.externalKnowledge) throw new Error('External knowledge runtime is unavailable');
218
+ return app.externalKnowledge;
219
+ }
220
+
221
+ function connectedKnowledge(app) {
222
+ if (!app.connectedKnowledge) throw new Error('Connected knowledge runtime is unavailable');
223
+ return app.connectedKnowledge;
224
+ }
225
+
185
226
  export function dispatchGraphTool(app, name, input) {
186
227
  const handler = HANDLERS[name];
187
228
  if (!handler) throw new Error(`Unknown agent tool: ${name}`);
@@ -0,0 +1,101 @@
1
+ const UI_MUTATION_PARITY = Object.freeze([
2
+ agent('POST /api/agent-attention', 'request_agent_attention'),
3
+ agent('POST /api/agent-attention/cancel', 'cancel_agent_attention'),
4
+ local('POST /api/agent-attention/respond', 'Only a local person may respond to an Agent request. A response records a decision, but never executes permissions or task acceptance.'),
5
+ agent('POST /api/system/runtime/leases', 'acquire_runtime_lease'),
6
+ agent('PATCH /api/system/runtime/leases/:leaseId', 'refresh_runtime_lease'),
7
+ agent('DELETE /api/system/runtime/leases/:leaseId', 'release_runtime_lease'),
8
+ agent('PATCH /api/capture-policy', 'update_capture_policy'),
9
+ local('PATCH /api/agent-access-policy', 'The Agent access kill switch requires local user presence and cannot be changed by an Agent.'),
10
+ local('PUT /api/system/settings', 'Device-level runtime settings remain a local user operation.'),
11
+ agent('POST /api/capture', 'capture_session_knowledge'),
12
+ agent('POST /api/subscriptions', 'subscribe_public_project'),
13
+ agent('DELETE /api/subscriptions/:projectId', 'unsubscribe_public_project'),
14
+ agent('POST /api/external-knowledge/bindings', 'create_external_knowledge_binding'),
15
+ agent('POST /api/external-knowledge/bindings/:bindingId/check', 'check_external_knowledge_binding'),
16
+ agent('POST /api/external-knowledge/bindings/:bindingId/sync', 'sync_external_knowledge_binding'),
17
+ agent('PATCH /api/external-knowledge/bindings/:bindingId/targets', 'update_external_knowledge_binding_targets'),
18
+ agent('DELETE /api/external-knowledge/bindings/:bindingId', 'delete_external_knowledge_binding'),
19
+ agent('PATCH /api/external-knowledge/conflict-policy', 'update_external_knowledge_conflict_policy'),
20
+ agent('PUT /api/personal-projects', 'upsert_personal_project'),
21
+ agent('PUT /api/project-agents', 'upsert_project_agent'),
22
+ agent('DELETE /api/project-agents/:agentId', 'delete_project_agent'),
23
+ agent('POST /api/project-agent-assignments', 'create_project_agent_assignment'),
24
+ agent('POST /api/project-agent-assignments/:assignmentId/end', 'end_project_agent_assignment'),
25
+ agent('POST /api/project-agent-assignments/:assignmentId/replace', 'replace_project_agent_assignment'),
26
+ agent('POST /api/project-agent-tasks', 'submit_project_agent_task'),
27
+ agent('POST /api/project-agent-activity', 'record_project_agent_task_activity'),
28
+ agent('PUT /api/project-agent-coordination-policy', 'update_project_agent_coordination_policy'),
29
+ agent('PATCH /api/project-agent-coordination-policy', 'update_project_agent_coordination_policy'),
30
+ agent('PUT /api/project-agent-recruitment-policy', 'update_project_agent_recruitment_policy'),
31
+ agent('PATCH /api/project-agent-recruitment-policy', 'update_project_agent_recruitment_policy'),
32
+ agent('POST /api/project-agent-recruitments/decision', 'decide_project_agent_recruitment'),
33
+ agent('PUT /api/executors', 'upsert_executor'),
34
+ agent('DELETE /api/executors/:executorId', 'delete_executor'),
35
+ agent('POST /api/executors/preflight', 'preflight_executor'),
36
+ agent('POST /api/executors/authorization', 'authorize_executor'),
37
+ agent('POST /api/executors/health', 'report_executor_health'),
38
+ agent('PUT /api/executor-routing-rules', 'upsert_executor_routing_rule'),
39
+ agent('PATCH /api/executor-routing-rules/:ruleId', 'update_executor_routing_rule'),
40
+ agent('DELETE /api/executor-routing-rules/:ruleId', 'delete_executor_routing_rule'),
41
+ agent('PATCH /api/project-agent-learning/:evidenceId', 'ignore_project_agent_routing_learning', 'reset_project_agent_routing_learning'),
42
+ agent('POST /api/projects/publish', 'publish_personal_project'),
43
+ agent('DELETE /api/projects/:projectId', 'delete_public_project'),
44
+ agent('POST /api/project-relations', 'create_project_relation'),
45
+ agent('POST /api/project-relations/:relationId/decision', 'review_project_relation'),
46
+ agent('POST /api/personal-review/:draftId/decision', 'review_personal_draft'),
47
+ agent('POST /api/review/:proposalId/decision', 'review_project_proposal'),
48
+ agent('POST /api/preference-conflicts/defer', 'defer_preference_conflict'),
49
+ local('POST /api/preference-conflicts/:conflictId/complete', 'Completing a preference conflict records a human decision and requires local user presence.'),
50
+ agent('PATCH /api/knowledge/:itemKind/:itemId', 'revise_personal_knowledge'),
51
+ agent('POST /api/knowledge/:itemKind/:itemId/assignment', 'reassign_personal_knowledge'),
52
+ local('POST /api/knowledge/:itemKind/:itemId/preference-scope', 'Changing personal preference visibility requires local human review.'),
53
+ agent('POST /api/knowledge/entity/:itemId/project-action/preview', 'preview_personal_project_action'),
54
+ agent('POST /api/knowledge/entity/:itemId/project-action', 'apply_personal_project_action'),
55
+ local('POST /api/knowledge/batch-confirmation', 'Batch confirmation asserts human confirmation and requires local user presence.'),
56
+ agent('POST /api/employee-templates/:templateId/recruit', 'recruit_employee'),
57
+ agent('POST /api/employee-templates/:templateId/call', 'call_employee_tool'),
58
+ local('PATCH /employee-workspaces/:templateId/:projectId/api/work-items/:itemId/status', 'Completing a Jefa work item records explicit human acceptance in the native workbench.'),
59
+ local('PATCH /employee-workspaces/:templateId/:projectId/api/projects/:projectId/sharing', 'Changing public project visibility requires explicit local human approval; the read-only report never grants access to private tasks.'),
60
+ ]);
61
+
62
+ export function agentInterfaceCatalog() {
63
+ const uiMutationParity = structuredClone(UI_MUTATION_PARITY);
64
+ const agentControlled = uiMutationParity.filter(({ access }) => access === 'agent').length;
65
+ return {
66
+ schemaVersion: 1,
67
+ contract: 'agent-interface-parity-v1',
68
+ guidance: [
69
+ 'Use the named Agent tool instead of browser automation for FULI data reads and writes.',
70
+ 'Read the tool schema and current record before writes; preserve versions, scopes, and idempotency keys.',
71
+ 'local_user_only entries are deliberate trust boundaries, not missing interfaces.',
72
+ ],
73
+ coverage: {
74
+ total: uiMutationParity.length,
75
+ agentControlled,
76
+ localUserOnly: uiMutationParity.length - agentControlled,
77
+ },
78
+ uiMutationParity,
79
+ readWorkflows: [
80
+ { domain: 'human-attention', toolNames: ['list_agent_attention', 'request_agent_attention', 'cancel_agent_attention'], guidance: 'Raise a hand only for an explicit human action. Read resolved responses before continuing. Responses do not grant permissions or accept tasks.' },
81
+ { domain: 'projects', toolNames: ['list_knowledge_spaces', 'list_personal_projects', 'list_project_releases', 'list_project_relations'] },
82
+ { domain: 'knowledge', toolNames: ['search_knowledge_graph', 'get_knowledge_graph', 'get_user_taste_skill', 'list_preference_conflicts'] },
83
+ { domain: 'people-and-tasks', toolNames: ['list_project_agents', 'list_project_agent_assignments', 'list_project_agent_tasks', 'view_project_agent_task', 'view_project_agent_activity', 'list_project_agent_recruitments'] },
84
+ { domain: 'employees-and-boards', toolNames: ['list_employee_templates', 'list_employee_tools', 'call_employee_tool'], guidance: 'Discover the employee read_board and other read tools before invoking them with exact project scope.' },
85
+ { domain: 'external-knowledge', toolNames: ['list_external_knowledge_connectors', 'discover_external_knowledge_sources', 'list_external_knowledge_bindings', 'retrieve_external_knowledge_binding', 'get_external_knowledge_conflict_policy'], readRoutes: ['POST /api/external-knowledge/discover', 'POST /api/external-knowledge/bindings/:bindingId/retrieve'] },
86
+ { domain: 'executors', toolNames: ['list_executors', 'get_executor', 'list_executor_routing_rules', 'list_project_agent_routing_learning'] },
87
+ { domain: 'review', toolNames: ['list_personal_review_queue', 'list_project_review_queue', 'list_knowledge_review_candidates'] },
88
+ { domain: 'policy-and-health', toolNames: ['get_capture_policy', 'get_project_agent_coordination_policy', 'get_project_agent_recruitment_policy', 'get_graphiti_status'] },
89
+ ],
90
+ };
91
+ }
92
+
93
+ function agent(route, ...toolNames) {
94
+ return Object.freeze(toolNames.length === 1
95
+ ? { route, access: 'agent', toolName: toolNames[0] }
96
+ : { route, access: 'agent', toolNames: Object.freeze(toolNames) });
97
+ }
98
+
99
+ function local(route, reason) {
100
+ return Object.freeze({ route, access: 'local_user_only', reason });
101
+ }
@@ -0,0 +1,184 @@
1
+ import {
2
+ arraySchema,
3
+ enumSchema,
4
+ integerSchema,
5
+ nullableStringSchema,
6
+ objectSchema,
7
+ stringSchema,
8
+ } from './schema.js';
9
+
10
+ const id = { ...stringSchema(), minLength: 1, maxLength: 256 };
11
+ const label = { ...stringSchema(), minLength: 1, maxLength: 512 };
12
+ const url = { ...stringSchema(), minLength: 1, maxLength: 2048, pattern: '^https?://\\S+$' };
13
+ const freeObject = { type: 'object', additionalProperties: true };
14
+ const target = objectSchema({
15
+ personalSpaceId: id,
16
+ personalProjectId: id,
17
+ mode: enumSchema(['hybrid', 'live', 'mirror']),
18
+ }, ['personalSpaceId', 'personalProjectId', 'mode']);
19
+
20
+ export const AGENT_INTERFACE_DEFINITIONS = [
21
+ {
22
+ name: 'list_project_agent_tasks',
23
+ title: 'READ · List project Agent tasks',
24
+ description: 'Read task summaries for one personal space, optionally narrowed to one exact project, Agent or status. Use this to discover task IDs before viewing or updating a task. Results are bounded; do not claim omitted historical tasks do not exist.',
25
+ inputSchema: objectSchema({
26
+ personalSpaceId: id,
27
+ personalProjectId: nullableStringSchema(),
28
+ agentId: nullableStringSchema(),
29
+ status: enumSchema(['awaiting_recruitment', 'queued', 'running', 'paused', 'failed', 'awaiting_review', 'blocked', 'completed', 'cancelled']),
30
+ limit: integerSchema({ minimum: 1, maximum: 200 }),
31
+ }, ['personalSpaceId']),
32
+ },
33
+ {
34
+ name: 'list_preference_conflicts',
35
+ title: 'READ · List preference conflicts',
36
+ description: 'Read recorded preference conflicts in one personal space before deferring or resolving an exact conflict. This does not confirm either instruction or apply a decision.',
37
+ inputSchema: objectSchema({
38
+ personalSpaceId: id,
39
+ status: nullableStringSchema(),
40
+ limit: integerSchema({ minimum: 1, maximum: 1000 }),
41
+ }, ['personalSpaceId']),
42
+ },
43
+ {
44
+ name: 'list_agent_interfaces',
45
+ title: 'READ · List Agent interface parity',
46
+ description: 'List the durable Agent-control contract for FULI UI data mutations and read workflows. Each operation names the exact structured tool to call, or an explicit local-user-only trust boundary. Use this catalog instead of browser automation when choosing how to read or change FULI data.',
47
+ inputSchema: objectSchema({}),
48
+ },
49
+ {
50
+ name: 'get_capture_policy',
51
+ title: 'READ · Get automatic capture policy',
52
+ description: 'Read whether automatic personal knowledge capture is enabled. This does not change the independent Agent access kill switch.',
53
+ inputSchema: objectSchema({}),
54
+ },
55
+ {
56
+ name: 'update_capture_policy',
57
+ title: 'WRITE · Update automatic capture policy',
58
+ description: 'On an explicit user request enable or disable automatic personal knowledge capture. This never changes Agent access, deletes existing knowledge, or bypasses project scope.',
59
+ inputSchema: objectSchema({ enabled: { type: 'boolean' } }, ['enabled']),
60
+ },
61
+ {
62
+ name: 'list_external_knowledge_connectors',
63
+ title: 'READ · List external knowledge connectors',
64
+ description: 'List installed external-knowledge connector types and capabilities. Returned availability does not authorize a connection or disclose credentials.',
65
+ inputSchema: objectSchema({}),
66
+ },
67
+ {
68
+ name: 'discover_external_knowledge_sources',
69
+ title: 'READ · Discover external knowledge sources',
70
+ description: 'Use one configured connector to discover source identifiers before creating a binding. Pass environment-variable references rather than credential values.',
71
+ inputSchema: objectSchema({
72
+ connectorType: id,
73
+ connectorConfig: freeObject,
74
+ source: freeObject,
75
+ query: nullableStringSchema(),
76
+ cursor: nullableStringSchema(),
77
+ limit: integerSchema({ minimum: 1, maximum: 100 }),
78
+ }, ['connectorType']),
79
+ },
80
+ {
81
+ name: 'list_external_knowledge_bindings',
82
+ title: 'READ · List external knowledge bindings',
83
+ description: 'List configured external-knowledge bindings, project targets, modes, capabilities, and sync state without returning secret values.',
84
+ inputSchema: objectSchema({}),
85
+ },
86
+ {
87
+ name: 'create_external_knowledge_binding',
88
+ title: 'WRITE · Create external knowledge binding',
89
+ description: 'Create a scoped external-knowledge binding after an explicit user request. Connector configuration may contain environment-variable names but never credential values. Every target must name an existing personal project and an explicit live, mirror, or hybrid mode.',
90
+ inputSchema: objectSchema({
91
+ name: label,
92
+ connectorType: id,
93
+ connectorConfig: freeObject,
94
+ source: freeObject,
95
+ targets: arraySchema(target, { minItems: 1, maxItems: 32 }),
96
+ }, ['name', 'connectorType', 'connectorConfig', 'source', 'targets']),
97
+ },
98
+ {
99
+ name: 'check_external_knowledge_binding',
100
+ title: 'WRITE · Check external knowledge binding',
101
+ description: 'Check one existing binding and persist its current readiness and negotiated capabilities. This does not synchronize content.',
102
+ inputSchema: objectSchema({ bindingId: id }, ['bindingId']),
103
+ },
104
+ {
105
+ name: 'sync_external_knowledge_binding',
106
+ title: 'WRITE · Synchronize external knowledge binding',
107
+ description: 'Synchronize selected mirror or hybrid targets through the configured connector. Preserve the binding scope and never widen targets implicitly.',
108
+ inputSchema: objectSchema({
109
+ bindingId: id,
110
+ targetId: nullableStringSchema(),
111
+ personalProjectId: nullableStringSchema(),
112
+ maxPages: integerSchema({ minimum: 1, maximum: 100 }),
113
+ pageSize: integerSchema({ minimum: 1, maximum: 100 }),
114
+ }, ['bindingId']),
115
+ },
116
+ {
117
+ name: 'retrieve_external_knowledge_binding',
118
+ title: 'READ · Retrieve external knowledge binding',
119
+ description: 'Run a scoped live retrieval against one binding target. This read does not store returned content or change project scope.',
120
+ inputSchema: objectSchema({
121
+ bindingId: id,
122
+ query: { ...stringSchema(), minLength: 1, maxLength: 2000 },
123
+ targetId: nullableStringSchema(),
124
+ personalProjectId: nullableStringSchema(),
125
+ limit: integerSchema({ minimum: 1, maximum: 100 }),
126
+ }, ['bindingId', 'query']),
127
+ },
128
+ {
129
+ name: 'update_external_knowledge_binding_targets',
130
+ title: 'WRITE · Update external knowledge targets',
131
+ description: 'Replace the explicit project targets for one binding. Removed targets are invalidated without deleting unrelated project knowledge; never infer or widen project access.',
132
+ inputSchema: objectSchema({
133
+ bindingId: id,
134
+ expectedTargetsVersion: { ...stringSchema(), minLength: 64, maxLength: 64, pattern: '^[a-f0-9]{64}$' },
135
+ targets: arraySchema(target, { minItems: 1, maxItems: 32 }),
136
+ }, ['bindingId', 'expectedTargetsVersion', 'targets']),
137
+ },
138
+ {
139
+ name: 'delete_external_knowledge_binding',
140
+ title: 'WRITE · Delete external knowledge binding',
141
+ description: 'Delete one explicitly identified external-knowledge binding after a user request. Its projected target state is invalidated while unrelated FULI knowledge is preserved.',
142
+ inputSchema: objectSchema({ bindingId: id }, ['bindingId']),
143
+ },
144
+ {
145
+ name: 'get_external_knowledge_conflict_policy',
146
+ title: 'READ · Get external knowledge conflict policy',
147
+ description: 'Read the conflict policy for one exact personal project.',
148
+ inputSchema: objectSchema({ personalProjectId: id }, ['personalProjectId']),
149
+ },
150
+ {
151
+ name: 'update_external_knowledge_conflict_policy',
152
+ title: 'WRITE · Update external knowledge conflict policy',
153
+ description: 'Set whether one exact personal project asks the human or lets the current Agent decide only the current response when external and graph knowledge conflict. This never confirms or rewrites either source.',
154
+ inputSchema: objectSchema({
155
+ personalSpaceId: id,
156
+ personalProjectId: id,
157
+ mode: enumSchema(['ask_human', 'agent_decide']),
158
+ }, ['personalSpaceId', 'personalProjectId', 'mode']),
159
+ },
160
+ {
161
+ name: 'defer_preference_conflict',
162
+ title: 'WRITE · Defer preference conflict',
163
+ description: 'Defer one exact preference conflict without resolving either side. The conflict remains visible for later review.',
164
+ inputSchema: objectSchema({
165
+ personalSpaceId: id,
166
+ conflictId: { ...stringSchema(), minLength: 1, maxLength: 1024 },
167
+ preferenceKey: label,
168
+ preferenceScope: enumSchema(['global', 'project']),
169
+ preferenceProjectId: nullableStringSchema(),
170
+ leftItemId: id,
171
+ leftItemKind: enumSchema(['entity', 'relationship']),
172
+ rightItemId: id,
173
+ rightItemKind: enumSchema(['entity', 'relationship']),
174
+ reason: { ...stringSchema(), minLength: 1, maxLength: 2000 },
175
+ }, ['personalSpaceId', 'conflictId', 'preferenceKey', 'preferenceScope',
176
+ 'leftItemId', 'leftItemKind', 'rightItemId', 'rightItemKind', 'reason']),
177
+ },
178
+ {
179
+ name: 'delete_public_project',
180
+ title: 'WRITE · Delete public project',
181
+ description: 'Delete one explicitly identified public project from its Provider after an explicit user request. This is not the same as unsubscribing and may require Provider ownership.',
182
+ inputSchema: objectSchema({ projectId: id, providerUrl: url }, ['projectId', 'providerUrl']),
183
+ },
184
+ ];
@@ -52,6 +52,7 @@ export const projectAgentModelStrategy = objectSchema({
52
52
  });
53
53
  export const projectAgentProfile = objectSchema({
54
54
  name: boundedString(160),
55
+ displayName: boundedString(160),
55
56
  responsibility: boundedString(4096),
56
57
  occupationEmoji: { ...nullableStringSchema(), minLength: 1, maxLength: 32 },
57
58
  agentType: enumSchema(['coordinator', 'durable', 'hr', 'temporary']),
@@ -125,7 +125,8 @@ function checkAgentSetup({ paths, env, nodePath, discover, inspect }) {
125
125
  runtimeConfigPath: paths.graphRuntimeConfigPath,
126
126
  sessionSkillPath: paths.sessionSkillPath,
127
127
  projectSkillPath: paths.projectSkillPath,
128
- reviewSkillPath: paths.reviewSkillPath
128
+ reviewSkillPath: paths.reviewSkillPath,
129
+ operationSkillPath: paths.operationSkillPath
129
130
  });
130
131
  return {
131
132
  status: 'checked',
@@ -0,0 +1,19 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "bole",
4
+ "version": "1.0.0",
5
+ "name": "Bole",
6
+ "role": "HR",
7
+ "description": "查看 Agent 人员分布、当前工作和带时间与原因的招募记录。",
8
+ "occupationEmoji": "🔎",
9
+ "defaultProjectScope": "all",
10
+ "workbench": { "kind": "native", "view": "people" },
11
+ "capabilities": ["Agent 招募", "人员分布", "工作状态", "招募审计"],
12
+ "workKinds": ["agent-recruitment", "staffing-review"],
13
+ "initialPreferences": [
14
+ "每次招募保留任务、原因、触发来源与时间线。",
15
+ "只展示 Provider 已记录的人员状态,不推测未上报的工作。"
16
+ ],
17
+ "permissions": ["agents.read", "tasks.read", "recruitments.read"],
18
+ "runtime": null
19
+ }
@@ -13,6 +13,9 @@ export function installEmployeePackage({ sourceDirectory, runtimeConfigPath, rep
13
13
  const source = resolve(sourceDirectory);
14
14
  const digest = packageDigest(source);
15
15
  const manifest = parseEmployeeManifest(JSON.parse(readFileSync(join(source, 'employee.json'), 'utf8')));
16
+ if (manifest.id === 'bole' || manifest.workbench?.kind === 'native') {
17
+ throw new EmployeeError('A built-in native workbench cannot be installed or replaced by a package', 409, 'native_workbench_reserved');
18
+ }
16
19
  if (manifest.runtime) {
17
20
  confinedPath(source, manifest.runtime.entry);
18
21
  confinedPath(source, `${manifest.runtime.webRoot}/index.html`);
@@ -27,6 +27,10 @@ export const employeeManifestSchema = z.object({
27
27
  boardTool: toolIdentifier,
28
28
  titleTool: toolIdentifier.optional(),
29
29
  }).strict().optional(),
30
+ workbench: z.object({
31
+ kind: z.literal('native'),
32
+ view: z.literal('people')
33
+ }).strict().optional(),
30
34
  runtime: z.object({
31
35
  apiVersion: z.literal(1),
32
36
  entry: relativeFile,
@@ -18,12 +18,14 @@ export function createEmployeePackageRegistry({ packageDirectory, dataDirectory,
18
18
  if (packageDirectory && existsSync(packageDirectory)) {
19
19
  for (const name of readdirSync(packageDirectory)) {
20
20
  if (!/^[a-z][a-z0-9-]{0,63}$/.test(name)) continue;
21
+ if (entries.get(name)?.manifest.workbench?.kind === 'native') continue;
21
22
  const root = join(packageDirectory, name);
22
23
  try {
23
24
  if (!statSync(root).isDirectory()) continue;
24
25
  confinedPath(packageDirectory, name);
25
26
  const manifest = readManifest(confinedPath(root, 'employee.json'));
26
27
  if (manifest.id !== name) continue;
28
+ if (manifest.workbench?.kind === 'native') continue;
27
29
  if (manifest.runtime) {
28
30
  confinedPath(root, manifest.runtime.entry);
29
31
  confinedPath(root, `${manifest.runtime.webRoot}/index.html`);
@@ -62,6 +62,9 @@ export function createEmployeeRecruitment({ app, registry, managementStore }) {
62
62
  async function recruit(input) {
63
63
  assertSpace(input);
64
64
  const { manifest } = registry.get(input.templateId);
65
+ if (manifest.workbench?.kind === 'native') {
66
+ throw new EmployeeError('This built-in role is provisioned by the Provider and does not require recruitment', 409, 'system_managed_employee');
67
+ }
65
68
  const selection = parseEmployeeProjectSelection(input);
66
69
  const agentId = employeeAgentId(manifest.id);
67
70
  const key = `${spaceId}:${agentId}`;
@@ -51,6 +51,9 @@ export function createEmployeeService({ app, runtimeConfigPath, registry: config
51
51
  if (!tool || !manifest.permissions.includes(tool.permission)) {
52
52
  throw new EmployeeError('Employee tool is not available or permitted', 403, 'tool_not_permitted');
53
53
  }
54
+ if (context.templateId === 'jefa' && tool.permission === 'board.write' && requestsHumanAcceptance(input.arguments)) {
55
+ throw new EmployeeError('Jefa completion requires explicit human acceptance in the workbench', 403, 'human_acceptance_required');
56
+ }
54
57
  const session = input.sourceSessionId ? {
55
58
  sourceApplication: input.sourceApplication ?? 'other', id: input.sourceSessionId,
56
59
  verified: input.sourceSessionVerified === true,
@@ -81,3 +84,10 @@ export function createEmployeeService({ app, runtimeConfigPath, registry: config
81
84
  async close() { try { await registry.close(); } finally { managementStore.close(); } }
82
85
  };
83
86
  }
87
+
88
+ function requestsHumanAcceptance(value, depth = 0) {
89
+ if (!value || typeof value !== 'object') return false;
90
+ if (depth > 32) throw new TypeError('Employee tool arguments are too deeply nested');
91
+ return Object.entries(value).some(([key, entry]) =>
92
+ (key === 'status' && entry === 'done') || requestsHumanAcceptance(entry, depth + 1));
93
+ }
@@ -1,4 +1,8 @@
1
1
  import { readJsonFile, writeJsonFileAtomic } from '../storage/json-file.js';
2
+ import { chmodSync, mkdirSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { DatabaseSync } from 'node:sqlite';
5
+ import { ApplicationError } from '../app/application-error.js';
2
6
 
3
7
  const CURRENT_VERSION = 2;
4
8
  const EMPTY_REGISTRY = Object.freeze({ version: CURRENT_VERSION, bindings: [] });
@@ -15,6 +19,26 @@ export class ExternalKnowledgeRegistry {
15
19
  return clone(this.#read().bindings);
16
20
  }
17
21
 
22
+ async withMutation(operation) {
23
+ // An OS-backed SQLite reservation coordinates separate console/MCP processes.
24
+ // No credentials or binding data is stored here; a crashed owner releases its lock.
25
+ mkdirSync(dirname(this.filePath), { recursive: true, mode: 0o700 });
26
+ const path = `${this.filePath}.mutation-lock.sqlite`;
27
+ const lock = new DatabaseSync(path);
28
+ try {
29
+ chmodSync(path, 0o600);
30
+ try { lock.exec('BEGIN IMMEDIATE'); }
31
+ catch (error) {
32
+ if (error.errcode === 5 || error.errcode === 6) {
33
+ throw new ApplicationError('external_knowledge_busy', 'Another external-knowledge mutation is in progress; re-read state before retrying');
34
+ }
35
+ throw error;
36
+ }
37
+ try { return await operation(); }
38
+ finally { lock.exec('ROLLBACK'); }
39
+ } finally { lock.close(); }
40
+ }
41
+
18
42
  get(id) {
19
43
  const binding = this.#read().bindings.find((item) => item.id === id);
20
44
  return binding ? clone(binding) : null;
@@ -9,6 +9,7 @@ import {
9
9
  normalizeExternalDocument
10
10
  } from './document-mapping.js';
11
11
  import { safeExternalSourceDiagnostic } from './safe-diagnostic.js';
12
+ import { ApplicationError } from '../app/application-error.js';
12
13
 
13
14
  const MODES = new Set(['hybrid', 'live', 'mirror']);
14
15
  const MAX_TARGETS = 32;
@@ -80,6 +81,10 @@ export class ExternalKnowledgeService {
80
81
  }
81
82
 
82
83
  async createBinding(input) {
84
+ return this.registry.withMutation(() => this.#createBinding(input));
85
+ }
86
+
87
+ async #createBinding(input) {
83
88
  const connectorType = requiredString(input?.connectorType, 'connectorType', 64);
84
89
  const connector = this.#connector(connectorType);
85
90
  const connectorConfig = safeConfiguration(input.connectorConfig ?? {});
@@ -117,7 +122,14 @@ export class ExternalKnowledgeService {
117
122
  }
118
123
 
119
124
  async updateBindingTargets(id, input) {
125
+ return this.registry.withMutation(() => this.#updateBindingTargets(id, input));
126
+ }
127
+
128
+ async #updateBindingTargets(id, input) {
120
129
  const binding = this.#binding(id);
130
+ if (input.expectedTargetsVersion !== undefined && input.expectedTargetsVersion !== targetsVersion(binding)) {
131
+ throw new ApplicationError('external_knowledge_conflict', 'Binding targets changed; re-read the complete targets array before retrying');
132
+ }
121
133
  const connector = this.#connector(binding.connectorType);
122
134
  const targets = bindingTargets(input, {
123
135
  bindingId: binding.id,
@@ -151,6 +163,10 @@ export class ExternalKnowledgeService {
151
163
  }
152
164
 
153
165
  async checkBinding(id) {
166
+ return this.registry.withMutation(() => this.#checkBinding(id));
167
+ }
168
+
169
+ async #checkBinding(id) {
154
170
  const binding = this.#binding(id);
155
171
  const connector = this.#connector(binding.connectorType);
156
172
  const result = typeof connector.check === 'function'
@@ -165,6 +181,10 @@ export class ExternalKnowledgeService {
165
181
  }
166
182
 
167
183
  async syncBinding(id, options = {}) {
184
+ return this.registry.withMutation(() => this.#syncBinding(id, options));
185
+ }
186
+
187
+ async #syncBinding(id, options) {
168
188
  if (!options || typeof options !== 'object' || Array.isArray(options)) {
169
189
  throw new TypeError('Synchronization options must be an object');
170
190
  }
@@ -227,6 +247,10 @@ export class ExternalKnowledgeService {
227
247
  }
228
248
 
229
249
  async deleteBinding(id) {
250
+ return this.registry.withMutation(() => this.#deleteBinding(id));
251
+ }
252
+
253
+ async #deleteBinding(id) {
230
254
  const binding = this.#binding(id);
231
255
  let invalidated = 0;
232
256
  for (const target of binding.targets) {
@@ -503,6 +527,7 @@ function publicBinding(binding) {
503
527
  const first = output.targets?.[0] ?? null;
504
528
  return {
505
529
  ...output,
530
+ targetsVersion: targetsVersion(binding),
506
531
  // Keep the v1 aliases while clients migrate to target-aware bindings.
507
532
  target: first ? {
508
533
  personalSpaceId: first.personalSpaceId,
@@ -513,6 +538,12 @@ function publicBinding(binding) {
513
538
  };
514
539
  }
515
540
 
541
+ function targetsVersion(binding) {
542
+ return createHash('sha256').update(JSON.stringify(binding.targets.map((target) => [
543
+ target.id, target.personalSpaceId, target.personalProjectId, target.mode,
544
+ ]).sort((left, right) => String(left[0]).localeCompare(String(right[0]))))).digest('hex');
545
+ }
546
+
516
547
  function bindingTargets(input, {
517
548
  bindingId,
518
549
  existingTargets = [],
@@ -631,13 +662,15 @@ function assertNegotiatedCapabilities(type, capabilities, targets) {
631
662
  }
632
663
 
633
664
  function selectedTargets(binding, input, { capability, requireOne = false }) {
665
+ const targetId = optionalString(input?.targetId, 'targetId', 256);
634
666
  const personalSpaceId = optionalString(input?.personalSpaceId, 'personalSpaceId', 256);
635
667
  const personalProjectId = optionalString(input?.personalProjectId, 'personalProjectId', 256);
636
668
  let targets = binding.targets.filter((target) =>
669
+ (!targetId || target.id === targetId) &&
637
670
  (!personalSpaceId || target.personalSpaceId === personalSpaceId) &&
638
671
  (!personalProjectId || target.personalProjectId === personalProjectId)
639
672
  );
640
- if ((personalSpaceId || personalProjectId) && !targets.length) {
673
+ if ((targetId || personalSpaceId || personalProjectId) && !targets.length) {
641
674
  throw new TypeError('External knowledge binding is not assigned to this personal project');
642
675
  }
643
676
  targets = targets.filter(({ mode }) => capability === 'sync' ? mode !== 'live' : mode !== 'mirror');
@@ -0,0 +1,26 @@
1
+ const fields = {
2
+ personalSpaceId: 'personal_space_id', personalProjectId: 'personal_project_id',
3
+ agentId: 'agent_id', taskId: 'task_id', requestId: 'request_id',
4
+ idempotencyKey: 'idempotency_key', requestedAction: 'requested_action',
5
+ expectedRevision: 'expected_revision', respondedBy: 'responded_by',
6
+ createdAt: 'created_at', updatedAt: 'updated_at'
7
+ };
8
+
9
+ export function attentionInput(input) {
10
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined)
11
+ .map(([key, value]) => [fields[key] ?? key, value]));
12
+ }
13
+
14
+ export function attentionRecord(value) {
15
+ const names = Object.fromEntries(Object.entries(fields).map(([camel, snake]) => [snake, camel]));
16
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [names[key] ?? key, item]));
17
+ }
18
+
19
+ export async function listAgentAttention(application, input) {
20
+ const value = await application.personal.listAgentAttention(attentionInput(input));
21
+ return { ...value, items: value.items.map(attentionRecord) };
22
+ }
23
+
24
+ export async function changeAgentAttention(application, operation, input) {
25
+ return attentionRecord(await application.personal.changeAgentAttention(operation, attentionInput(input)));
26
+ }
@@ -61,6 +61,7 @@ export async function beginTaskContext(application, {
61
61
  previous_checkpoint_missing: task.previousCheckpointMissing,
62
62
  ...preferences,
63
63
  task_guidance: {
64
+ human_attention: 'When the selected Project Agent needs a human answer, decision, review or permission, use request_agent_attention with the exact current space/project/Agent IDs, a stable idempotency key and a specific requestedAction. Never raise a hand for ordinary running, queueing or automatic retries. Read list_agent_attention with status=resolved for replies; cancel obsolete requests with cancel_agent_attention. A response does not grant permission or accept task completion.',
64
65
  retrieval: 'Inspect task_knowledge_recall before asking for a stable project fact or method again. On a miss, use search_current_project_knowledge with focused action, artifact, target-system, or identifier queries; never use the full conversational request as the only query.',
65
66
  checkpoint: 'Before finishing, call checkpoint_task_knowledge with capture_candidates or retain_nothing. When durable role context changed, include agentMemory with the loaded revision and a bounded merged summary, decisions, open threads and next actions. Do not overwrite from truncated context or store raw transcripts.'
66
67
  }