fuli-context 0.10.0 → 0.11.0

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 (201) hide show
  1. package/README.md +27 -30
  2. package/README.zh-CN.md +22 -26
  3. package/dist/web/assets/AboutPage-CKq4IRHd.js +1 -0
  4. package/dist/web/assets/AgentConversations-DfXiFvBN.js +2 -0
  5. package/dist/web/assets/AgentConversations-XqNEBY_2.css +1 -0
  6. package/dist/web/assets/AgentHand-BBM-sKh2.js +1 -0
  7. package/dist/web/assets/AgentName-C_6R80nP.js +1 -0
  8. package/dist/web/assets/AgentProfilePage-Cz-i1fRD.js +1 -0
  9. package/dist/web/assets/AgentsDirectoryPage-BI0EFz6y.js +1 -0
  10. package/dist/web/assets/AgentsDirectoryPage-yRWktiin.css +1 -0
  11. package/dist/web/assets/ConnectionsPage-BoC6kXgs.js +1 -0
  12. package/dist/web/assets/ConnectionsPage-CM9C-zEI.css +1 -0
  13. package/dist/web/assets/EmployeeRecruitDialog-BVyclQAJ.js +1 -0
  14. package/dist/web/assets/EmployeeRecruitDialog-CVbqCzHb.css +1 -0
  15. package/dist/web/assets/EmployeeWorkbenchPage-D-c_Jm2z.css +1 -0
  16. package/dist/web/assets/EmployeeWorkbenchPage-D2GvYraI.js +3 -0
  17. package/dist/web/assets/KnowledgeEditDialog-a0AKWics.js +1 -0
  18. package/dist/web/assets/KnowledgeEditDialog-hT9znkoe.css +1 -0
  19. package/dist/web/assets/KnowledgeInspector-DMg9yJZ2.js +1 -0
  20. package/dist/web/assets/KnowledgeOrganizerPage-BgsHM3Qw.js +1 -0
  21. package/dist/web/assets/KnowledgeOrganizerPage-DeL7Jp34.css +1 -0
  22. package/dist/web/assets/KnowledgePage-kn8MqUCO.js +1 -0
  23. package/dist/web/assets/KnowledgeWorkspace-BtRq9fvC.js +4 -0
  24. package/dist/web/assets/KnowledgeWorkspace-D4a7FcAb.css +1 -0
  25. package/dist/web/assets/NotFoundPage-D-7jLz3o.js +1 -0
  26. package/dist/web/assets/OverviewPage-BHPc-XxY.js +1 -0
  27. package/dist/web/assets/OverviewPage-By-fUzSG.css +1 -0
  28. package/dist/web/assets/PersonalProfilePage-C00iV0Kx.js +1 -0
  29. package/dist/web/assets/PersonalProfilePage-DrMDAAdy.css +1 -0
  30. package/dist/web/assets/PersonalProjectsPage-CFNkdHSo.js +1 -0
  31. package/dist/web/assets/ProjectAgentFirstTask-Bi1ZsTC1.css +1 -0
  32. package/dist/web/assets/ProjectAgentFirstTask-CrCWEYgn.js +1 -0
  33. package/dist/web/assets/ProjectAgentsPage-BzztLMH-.js +12 -0
  34. package/dist/web/assets/ProjectAgentsPage-DNLNjYp-.css +1 -0
  35. package/dist/web/assets/ProjectScopePicker-CMxn3txv.js +1 -0
  36. package/dist/web/assets/PublicProjectsPage-DKamXDV9.css +1 -0
  37. package/dist/web/assets/PublicProjectsPage-DQVtvhOS.js +1 -0
  38. package/dist/web/assets/ReviewPage-BZhvyDWc.js +1 -0
  39. package/dist/web/assets/RoundtablesPage-CVebTXQa.js +1 -0
  40. package/dist/web/assets/RoundtablesPage-Cb0R4GaM.css +1 -0
  41. package/dist/web/assets/SettingsPage-1knvfOW6.js +1 -0
  42. package/dist/web/assets/SettingsPage-BmYkLul4.css +1 -0
  43. package/dist/web/assets/SharedJefaReportPage-BCVq8u0P.js +1 -0
  44. package/dist/web/assets/UiDialog-Da1P10f4.js +1 -0
  45. package/dist/web/assets/UiDisclosure-8wE8YuE4.js +1 -0
  46. package/dist/web/assets/UiSegmented-BKZXc8QF.js +1 -0
  47. package/dist/web/assets/UiSelect-Bv6R0yr1.css +1 -0
  48. package/dist/web/assets/UiSelect-D5Xj5Ec4.js +1 -0
  49. package/dist/web/assets/WritingTastePage-B8-etOBn.js +1 -0
  50. package/dist/web/assets/WritingTastePage-B_zYD1df.css +1 -0
  51. package/dist/web/assets/agent-profile-BZj3ezWe.css +1 -0
  52. package/dist/web/assets/{attention-store-Dk-_P4Oi.js → attention-store-BfxtL9EV.js} +1 -1
  53. package/dist/web/assets/{catalog-DN72452f.js → catalog-6SN9i3WR.js} +1 -1
  54. package/dist/web/assets/client-CUTkF3t4.js +1 -0
  55. package/dist/web/assets/{console-BtPqo79j.js → console-CG8tdtSs.js} +1 -1
  56. package/dist/web/assets/{conversation-launcher-settings-jkHRmWai.js → conversation-launcher-settings-CyZkLJg7.js} +1 -1
  57. package/dist/web/assets/{i18n-D2CJkEYK.js → i18n-CCMR4gDS.js} +3 -3
  58. package/dist/web/assets/{identity-B3zEw-1u.js → identity-C1eN-FSB.js} +1 -1
  59. package/dist/web/assets/index-kPB0Cf8Q.css +1 -0
  60. package/dist/web/assets/index-sLDKI6R-.js +5 -0
  61. package/dist/web/assets/{model-S7ehAjky.js → model-l-sAqtW_.js} +1 -1
  62. package/dist/web/assets/{personal-profile-graph--GG3Mizo.js → personal-profile-graph-BD1Y6TDS.js} +1 -1
  63. package/dist/web/assets/shared-report-B46dxlCR.js +1 -0
  64. package/dist/web/assets/{useAgentRoster-BAPKWZ9y.js → useAgentRoster-DdxoKDEE.js} +1 -1
  65. package/dist/web/assets/{useApi-CROJJdhE-BtFMXFjw.js → useApi-CROJJdhE-DlOiu8lP.js} +1 -1
  66. package/dist/web/assets/{vue-router-DkaTtPn0.js → vue-router-DjqEU4W2.js} +1 -1
  67. package/dist/web/index.html +14 -16
  68. package/docs/agent-conversations-and-collaboration.md +9 -9
  69. package/docs/agent-roundtable.md +61 -0
  70. package/docs/employee-agents.md +2 -2
  71. package/docs/project-agent-memory.md +1 -1
  72. package/graph-provider/fuli_graph/agent_conversation_models.py +11 -2
  73. package/graph-provider/fuli_graph/conversation_context.py +40 -9
  74. package/graph-provider/fuli_graph/default_project_lead.py +88 -0
  75. package/graph-provider/fuli_graph/project_agent_memory_routes.py +7 -0
  76. package/graph-provider/fuli_graph/project_agent_task_models.py +3 -0
  77. package/graph-provider/fuli_graph/runtime.py +3 -76
  78. package/graph-provider/fuli_graph/store.py +2 -0
  79. package/graph-provider/fuli_graph/store_agent_conversations.py +60 -8
  80. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +3 -1
  81. package/graph-provider/fuli_graph/store_project_agent_tasks.py +12 -8
  82. package/graph-provider/fuli_graph/store_project_agents.py +25 -124
  83. package/graph-provider/fuli_graph/store_task_contexts.py +26 -0
  84. package/graph-provider/fuli_graph/system_agents.py +128 -0
  85. package/graph-provider/fuli_graph/system_hr_identity.py +1 -1
  86. package/graph-provider/fuli_graph/task_context_routes.py +10 -0
  87. package/graph-provider/pyproject.toml +1 -1
  88. package/npm-shrinkwrap.json +2 -2
  89. package/package.json +2 -2
  90. package/src/agent-roundtable/http.js +14 -0
  91. package/src/agent-roundtable/service.js +218 -0
  92. package/src/agent-roundtable/store.js +86 -0
  93. package/src/agent-roundtable/tools.js +61 -0
  94. package/src/agent-roundtable/wake.js +99 -0
  95. package/src/agent-tools/agent-conversation-definitions.js +3 -3
  96. package/src/agent-tools/graph-definitions.js +3 -2
  97. package/src/agent-tools/interface-catalog.js +0 -5
  98. package/src/agent-tools.js +2 -2
  99. package/src/agents/claude-code/wake.js +37 -0
  100. package/src/agents/codex/wake.js +47 -0
  101. package/src/agents/identity-receipt.js +16 -6
  102. package/src/agents/session-log.js +34 -0
  103. package/src/agents/wake-registry.js +14 -0
  104. package/src/cli/command-registry.js +0 -2
  105. package/src/cli/setup-command.js +2 -5
  106. package/src/cli/update-command.js +13 -2
  107. package/src/cli.js +0 -5
  108. package/src/employees/management-policy.js +4 -2
  109. package/src/employees/manifest.js +3 -0
  110. package/src/employees/recruitment.js +3 -2
  111. package/src/graphiti/agent-conversations.js +11 -9
  112. package/src/graphiti/agent-knowledge-workflows.js +34 -7
  113. package/src/graphiti/federated-application.js +13 -5
  114. package/src/graphiti/project-agent-task-entry.js +19 -7
  115. package/src/graphiti/project-path-context.js +7 -2
  116. package/src/graphiti/provider-client.js +8 -0
  117. package/src/graphiti/repository-project-registration.js +17 -0
  118. package/src/graphiti/task-entry-preferences.js +5 -0
  119. package/src/http/api-router.js +5 -3
  120. package/src/http/error-mapping.js +0 -2
  121. package/src/mcp/instructions.js +5 -5
  122. package/src/mcp/session-id.js +1 -1
  123. package/src/mcp/tool-annotations.js +3 -4
  124. package/src/setup/agents.js +1 -41
  125. package/src/setup/graph-runtime.js +15 -2
  126. package/src/setup/session-skill.js +22 -2
  127. package/dist/web/assets/AboutPage-D-GyJNv1.js +0 -1
  128. package/dist/web/assets/AgentConversations-DOuZgUeA.js +0 -2
  129. package/dist/web/assets/AgentConversations-DS8h3yKn.css +0 -1
  130. package/dist/web/assets/AgentHand-eGsXoGSq.js +0 -1
  131. package/dist/web/assets/AgentName-Dtmu9Ff5.js +0 -1
  132. package/dist/web/assets/AgentProfilePage-D8Tvayn7.js +0 -1
  133. package/dist/web/assets/AgentsDirectoryPage-BTEfKZjF.js +0 -1
  134. package/dist/web/assets/ConnectionsPage-C1w99yN3.css +0 -1
  135. package/dist/web/assets/ConnectionsPage-DwOIcvl9.js +0 -1
  136. package/dist/web/assets/EmployeeRecruitDialog-BrIMRblq.css +0 -1
  137. package/dist/web/assets/EmployeeRecruitDialog-D11blfwU.js +0 -1
  138. package/dist/web/assets/EmployeeWorkbenchPage-BcEzYt65.css +0 -1
  139. package/dist/web/assets/EmployeeWorkbenchPage-CK2BAoGt.js +0 -3
  140. package/dist/web/assets/GrowthLoading-KTjE2e50.js +0 -1
  141. package/dist/web/assets/KnowledgeEditDialog-B3x8vs3c.css +0 -1
  142. package/dist/web/assets/KnowledgeEditDialog-n9trHQ5d.js +0 -1
  143. package/dist/web/assets/KnowledgeInspector-BcNjgw9H.js +0 -1
  144. package/dist/web/assets/KnowledgeOrganizerPage-BzgxP-nG.css +0 -1
  145. package/dist/web/assets/KnowledgeOrganizerPage-DMBpGxLr.js +0 -1
  146. package/dist/web/assets/KnowledgePage-POkCMW8f.js +0 -1
  147. package/dist/web/assets/KnowledgeWorkspace-2FvQadhB.js +0 -4
  148. package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +0 -1
  149. package/dist/web/assets/NotFoundPage-BCMZbeB8.js +0 -1
  150. package/dist/web/assets/OverviewPage-0CFWCG9-.css +0 -1
  151. package/dist/web/assets/OverviewPage-0mYifpjp.js +0 -1
  152. package/dist/web/assets/PersonalProfilePage-Chv2a2pi.css +0 -1
  153. package/dist/web/assets/PersonalProfilePage-JW6Muix6.js +0 -1
  154. package/dist/web/assets/PersonalProjectsPage-CRbi64JP.js +0 -1
  155. package/dist/web/assets/ProjectAgentFirstTask-6afJ8HUe.css +0 -1
  156. package/dist/web/assets/ProjectAgentFirstTask-DJ0wTbh8.js +0 -1
  157. package/dist/web/assets/ProjectAgentsPage-CPgKVLgg.js +0 -12
  158. package/dist/web/assets/ProjectAgentsPage-DeuRkMp1.css +0 -1
  159. package/dist/web/assets/ProjectScopePicker-BwB7gK-7.js +0 -1
  160. package/dist/web/assets/PublicProjectsPage-CB272Sq-.js +0 -1
  161. package/dist/web/assets/ReviewPage-CelTZU4C.js +0 -1
  162. package/dist/web/assets/RoundtablesPage-C9dXfWCl.js +0 -2
  163. package/dist/web/assets/RoundtablesPage-DCbIBnBq.css +0 -1
  164. package/dist/web/assets/SearchableSelect-f7EHbyDL.js +0 -1
  165. package/dist/web/assets/SettingsPage-5g7mRN2F.css +0 -1
  166. package/dist/web/assets/SettingsPage-nAijmm5f.js +0 -1
  167. package/dist/web/assets/SharedJefaReportPage-CjpJ9bkk.js +0 -1
  168. package/dist/web/assets/UiDisclosure-CWlC91Zt.js +0 -1
  169. package/dist/web/assets/WritingTastePage-DaHWY61P.js +0 -1
  170. package/dist/web/assets/WritingTastePage-SMGF39mf.css +0 -1
  171. package/dist/web/assets/agent-profile-CBvJHGjt.css +0 -1
  172. package/dist/web/assets/client-B2cRl-7c.js +0 -1
  173. package/dist/web/assets/copy-D2LqwtPZ.js +0 -1
  174. package/dist/web/assets/index-CNp0tRNp.css +0 -1
  175. package/dist/web/assets/index-ulISYH02.js +0 -5
  176. package/dist/web/assets/shared-report-DEK9wvhl.js +0 -1
  177. package/dist/web/assets/useModalDialog-D8tiJfbc.js +0 -1
  178. package/docs/roundtable.md +0 -46
  179. package/src/agents/a2a/roundtable-participant.js +0 -374
  180. package/src/agents/claude-code/roundtable-participant.js +0 -24
  181. package/src/agents/codex/roundtable-config.js +0 -32
  182. package/src/agents/codex/roundtable-participant.js +0 -34
  183. package/src/agents/grok/roundtable-participant.js +0 -185
  184. package/src/agents/pi/roundtable-participant.js +0 -193
  185. package/src/agents/pi/roundtable-result-format.js +0 -183
  186. package/src/cli/roundtable-command.js +0 -79
  187. package/src/roundtables/application.js +0 -69
  188. package/src/roundtables/domain.js +0 -181
  189. package/src/roundtables/http-router.js +0 -58
  190. package/src/roundtables/mcp-server.js +0 -55
  191. package/src/roundtables/participant-registry.js +0 -12
  192. package/src/roundtables/process.js +0 -70
  193. package/src/roundtables/profiles.js +0 -84
  194. package/src/roundtables/result-contract.js +0 -47
  195. package/src/roundtables/server.js +0 -52
  196. package/src/roundtables/service.js +0 -480
  197. package/src/roundtables/store.js +0 -63
  198. package/src/roundtables/tool-contract.js +0 -58
  199. package/src/roundtables/turn-prompt.js +0 -24
  200. package/src/roundtables/worker.js +0 -85
  201. /package/dist/web/assets/{GrowthLoading-CIuQ1icy.css → client-CIuQ1icy.css} +0 -0
@@ -12,20 +12,30 @@ export function agentContinuation(projectId, agentId, conversationId = null) {
12
12
  guidance: 'Use a connected client and the same authorized project/Agent. Obtain a fresh task context; never reuse the previous client’s token. This is a FULI prompt, not a native client @ picker registration.' };
13
13
  }
14
14
 
15
- export function taskAgentReceipt(application, { projectId, agentId, name = null,
16
- sourceApplication = null, workStatus = null, conversationId = null, persistence = null }) {
17
- const owner = agentId ? { agent_id: agentId, name,
18
- profile_url: agentProfileUrl(application, agentId), source_application: sourceApplication } : null;
15
+ function agentLink(application, agentId, name) {
19
16
  const label = String(name?.trim() || agentId).replace(/[\r\n\t]+/g, ' ')
20
17
  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
21
18
  .replace(/[\\`*_{}\[\]()!|]/g, '\\$&');
19
+ return `[${label}](<${agentProfileUrl(application, agentId).replace(/[<>\s]/g, encodeURIComponent)}>)`;
20
+ }
21
+
22
+ // The reply's attribution line: who led the task and who actually helped.
23
+ export function taskAgentReceipt(application, { projectId, agentId, name = null,
24
+ sourceApplication = null, workStatus = null, conversationId = null, persistence = null,
25
+ collaborators = [] }) {
26
+ const owner = agentId ? { agent_id: agentId, name,
27
+ profile_url: agentProfileUrl(application, agentId), source_application: sourceApplication } : null;
28
+ const helpers = owner ? collaborators.filter(item => item.agentId !== agentId) : [];
22
29
  const markdown = owner
23
- ? `FULI Agent:[${label}](<${owner.profile_url.replace(/[<>\s]/g, encodeURIComponent)}>)`
30
+ ? `FULI Agent:负责人 ${agentLink(application, agentId, name)}`
31
+ + (helpers.length ? ` · 协作 ${helpers.map(item => agentLink(application, item.agentId, item.name)).join('、')}` : '')
24
32
  : 'FULI Agent:未选定';
25
33
  return { required: true, markdown, owner,
34
+ collaborators: helpers.map(item => ({ agent_id: item.agentId, name: item.name,
35
+ profile_url: agentProfileUrl(application, item.agentId) })),
26
36
  personal_project_id: projectId, work_status: workStatus, persistence,
27
37
  continuation: owner ? agentContinuation(projectId, agentId, conversationId) : null,
28
- guidance: 'MUST include agent_receipt.markdown unchanged exactly once in every final user-visible reply, including greetings, status and errors, even when capture is disabled or nothing is retained. Use this task’s receipt; never invent an owner. Report only truthful work/memory status and actually used collaboration_receipt participants; tools and configured peers are not independent model workers.' };
38
+ guidance: 'MUST end every final user-visible reply with agent_receipt.markdown unchanged, exactly once, including greetings, status and errors, even when capture is disabled or nothing is retained. The receipt returned by checkpoint_task_knowledge supersedes the entry receipt because it names actual collaborators. Never invent an owner or a collaborator.' };
29
39
  }
30
40
 
31
41
  export function employeeCollaborationReceipt(application, context, { tool, permission, sourceApplication }) {
@@ -0,0 +1,34 @@
1
+ import { closeSync, openSync, readSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ // Read-only helpers for locating a client's JSONL session log on disk.
5
+
6
+ export function listDirectory(directory) {
7
+ try { return readdirSync(directory); } catch { return []; }
8
+ }
9
+
10
+ // Newest-first search for a file ending in `suffix`, at most `depth` levels down.
11
+ export function findFile(directory, suffix, depth) {
12
+ for (const entry of listDirectory(directory).sort().reverse()) {
13
+ const path = join(directory, entry);
14
+ if (entry.endsWith(suffix)) return path;
15
+ if (depth > 0 && !entry.includes('.')) {
16
+ const found = findFile(path, suffix, depth - 1);
17
+ if (found) return found;
18
+ }
19
+ }
20
+ return null;
21
+ }
22
+
23
+ // The first non-empty string `pick` returns for a JSON line near the start of the log.
24
+ export function firstField(file, pick) {
25
+ const handle = openSync(file, 'r');
26
+ try {
27
+ const buffer = Buffer.alloc(256 * 1024);
28
+ const length = readSync(handle, buffer, 0, buffer.length, 0);
29
+ for (const line of buffer.subarray(0, length).toString('utf8').split('\n')) {
30
+ try { const value = pick(JSON.parse(line)); if (typeof value === 'string' && value) return value; } catch { /* partial line */ }
31
+ }
32
+ return null;
33
+ } finally { closeSync(handle); }
34
+ }
@@ -0,0 +1,14 @@
1
+ import { claudeCodeWake } from './claude-code/wake.js';
2
+ import { codexWake } from './codex/wake.js';
3
+
4
+ // Clients that can be woken headlessly to answer another Agent, in preference order.
5
+ export const WAKE_ADAPTERS = Object.freeze({
6
+ [claudeCodeWake.id]: claudeCodeWake,
7
+ [codexWake.id]: codexWake,
8
+ });
9
+
10
+ export const WAKE_CLIENTS = Object.freeze(Object.keys(WAKE_ADAPTERS));
11
+
12
+ export function wakeAdapter(client) {
13
+ return Object.hasOwn(WAKE_ADAPTERS, client) ? WAKE_ADAPTERS[client] : null;
14
+ }
@@ -12,8 +12,6 @@ General:
12
12
  --version, -v
13
13
 
14
14
  Local service:
15
- roundtable serve [--port PORT] [--data-dir DIR] [--public-url HTTPS_URL]
16
- roundtable worker --url URL --room ID --runtime codex|claude-code|pi|grok|a2a [--workspace DIR] [--allow-write] [--model MODEL]
17
15
  start [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME] (checks Agent setup)
18
16
  stop [--data-dir DIR]
19
17
  restart [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
@@ -96,12 +96,9 @@ export function formatSetupResult(result) {
96
96
  }
97
97
  lines.push('Knowledge storage: Graphiti / Neo4j');
98
98
  for (const agent of result.agents) {
99
- if (agent.status === 'partial') {
100
- lines.push(`${agent.label}: partially configured; retry setup to finish installation`);
101
- continue;
102
- }
103
99
  if (agent.status !== 'connected') {
104
- lines.push(`${agent.label}: connection failed; retry setup later`);
100
+ const state = agent.status === 'partial' ? 'partially configured' : 'connection failed';
101
+ lines.push(`${agent.label}: ${state}${agent.message ? ` (${agent.message})` : ''}; retry fuli setup`);
105
102
  continue;
106
103
  }
107
104
  lines.push(agent.newTaskRequired
@@ -234,11 +234,21 @@ function describeProcessFailure(result) {
234
234
  return 'unknown process error';
235
235
  }
236
236
 
237
+ // Node refuses to spawn Windows batch files such as npm.cmd without a shell
238
+ // (CVE-2024-27980). Only fixed, shell-inert arguments may take that path.
239
+ export function batchFileSpawnOptions(command, args) {
240
+ if (!/\.(?:cmd|bat)$/i.test(command)) return {};
241
+ const unsafe = args.find((arg) => !/^[\w@./:=-]+$/.test(arg));
242
+ if (unsafe !== undefined) throw new TypeError(`Refusing to pass a shell-sensitive argument to ${command}`);
243
+ return { shell: true };
244
+ }
245
+
237
246
  function spawnInherited(command, args, { env }) {
238
247
  return spawnSync(command, args, {
239
248
  env,
240
249
  stdio: 'inherit',
241
- windowsHide: true
250
+ windowsHide: true,
251
+ ...batchFileSpawnOptions(command, args)
242
252
  });
243
253
  }
244
254
 
@@ -247,7 +257,8 @@ function spawnCaptured(command, args, { env }) {
247
257
  env,
248
258
  encoding: 'utf8',
249
259
  stdio: ['ignore', 'pipe', 'pipe'],
250
- windowsHide: true
260
+ windowsHide: true,
261
+ ...batchFileSpawnOptions(command, args)
251
262
  });
252
263
  }
253
264
 
package/src/cli.js CHANGED
@@ -10,7 +10,6 @@ import { runLocalRuntimeCommand } from './cli/local-runtime-command.js';
10
10
  import { runRemoteMcpCommand } from './cli/remote-mcp-command.js';
11
11
  import { runGraphDataCommand } from './cli/graph-data-command.js';
12
12
  import { runEmployeeCommand } from './cli/employee-command.js';
13
- import { runRoundtableCommand } from './cli/roundtable-command.js';
14
13
  import { runSetupCommand } from './cli/setup-command.js';
15
14
  import { runUninstallCommand } from './cli/uninstall-command.js';
16
15
  import { runUpdateCommand } from './cli/update-command.js';
@@ -42,10 +41,6 @@ export async function main(argv = process.argv.slice(2), env = process.env) {
42
41
  assertSupportedNodeVersion();
43
42
  return runEmployeeCommand(commandArgs, { env });
44
43
  }
45
- if (command === 'roundtable') {
46
- assertSupportedNodeVersion();
47
- return runRoundtableCommand(commandArgs, { env });
48
- }
49
44
  if (command === 'connect-workspace') {
50
45
  assertSupportedNodeVersion();
51
46
  return runWorkspaceConnectionCommand(commandArgs, { env });
@@ -2,7 +2,7 @@ import { mkdirSync } from 'node:fs';
2
2
  import { dirname } from 'node:path';
3
3
  import { DatabaseSync } from 'node:sqlite';
4
4
  import { z } from 'zod';
5
- import { EmployeeError } from './manifest.js';
5
+ import { EmployeeError, FIXED_EMPLOYEE_AGENT_TYPES } from './manifest.js';
6
6
 
7
7
  const projectId = z.string().min(1).max(256);
8
8
  export const employeeManagementSchema = z.object({
@@ -73,7 +73,9 @@ export function managementFor(manifest, agent, stored) {
73
73
  }
74
74
  // Upgrades preserve existing authorization. Only a new recruitment uses the
75
75
  // template default; an existing employee opts in by saving the new all rule.
76
- return { revision: 0, mode: agent ? 'selected' : manifest.defaultProjectScope ?? 'selected',
76
+ // A fixed role nobody has assigned yet is still in its template default.
77
+ const configured = agent && !(Object.hasOwn(FIXED_EMPLOYEE_AGENT_TYPES, manifest.id) && !agent.assignments?.length);
78
+ return { revision: 0, mode: configured ? 'selected' : manifest.defaultProjectScope ?? 'selected',
77
79
  projectIds: [...new Set((agent?.assignments ?? []).filter(a => a.status === 'active').map(a => a.personalProjectId))],
78
80
  excludedProjectIds: [], titleMode: manifest.taskEntry ? 'auto' : 'suggest', titleStyle: 'emoji' };
79
81
  }
@@ -44,6 +44,9 @@ export function parseEmployeeManifest(value) {
44
44
  return parsed.data;
45
45
  }
46
46
 
47
+ // Built-in roles every personal space has; the Provider provisions their identities.
48
+ export const FIXED_EMPLOYEE_AGENT_TYPES = Object.freeze({ jefa: 'coordinator', bole: 'hr' });
49
+
47
50
  export const employeeAgentId = (id) => `employee.${identifier.parse(id)}`;
48
51
  export const employeeCapability = (id) => `fuli.employee:${identifier.parse(id)}`;
49
52
 
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { EmployeeError, employeeAgentId, employeeCapability } from './manifest.js';
2
+ import { EmployeeError, FIXED_EMPLOYEE_AGENT_TYPES, employeeAgentId, employeeCapability } from './manifest.js';
3
3
  import {
4
4
  activeEmployeeAssignments, employeeAssignmentsVersion,
5
5
  parseEmployeeProjectSelection, resolveEmployeeProjectSelection,
@@ -50,6 +50,7 @@ export function createEmployeeRecruitment({ app, registry, managementStore }) {
50
50
  runtimeStatus,
51
51
  agentId: owned ? agent.agentId : null,
52
52
  agentStatus: owned ? agent.profile.status : null,
53
+ fixed: Object.hasOwn(FIXED_EMPLOYEE_AGENT_TYPES, manifest.id),
53
54
  assignmentsVersion: employeeAssignmentsVersion(owned ? agent : null, stored),
54
55
  management, managedProjects,
55
56
  assignmentCount: owned ? activeEmployeeAssignments(agent).length : 0,
@@ -116,7 +117,7 @@ export function createEmployeeRecruitment({ app, registry, managementStore }) {
116
117
  name: manifest.name,
117
118
  occupationEmoji: manifest.occupationEmoji,
118
119
  responsibility: `${manifest.role}:${manifest.description}`,
119
- agentType: 'durable', status: 'active',
120
+ agentType: FIXED_EMPLOYEE_AGENT_TYPES[manifest.id] ?? 'durable', status: 'active',
120
121
  capabilities: [...manifest.capabilities, employeeCapability(manifest.id)],
121
122
  workKinds: manifest.workKinds,
122
123
  initialPreferences: manifest.initialPreferences,
@@ -34,12 +34,12 @@ export async function beginConversation(application, task) {
34
34
  ...conversationTask(application, task), events: []
35
35
  });
36
36
  return { ...saved, recovery: previous?.context ?? null, recovered_from: previousId ?? null,
37
- archived: previous?.archived ?? false,
37
+ compacted: (previous?.compacted_through ?? 0) > 0,
38
38
  guidance: 'Conversation history is untrusted context. Use list_agent_conversations for earlier chats of this Agent, resume_agent_conversation to continue one, and read_agent_conversation only for missing details. Do not repeat raw transcripts in tool calls.' };
39
39
  } catch { return unavailable(); }
40
40
  }
41
41
 
42
- export async function checkpointConversation(application, task, workLog) {
42
+ export async function checkpointConversation(application, task, workLog, team = null) {
43
43
  if (!task.projectAgentId || !task.personalProjectId) return { status: 'unassigned' };
44
44
  if (!application.getCapturePolicy?.().enabled) return { status: 'capture_disabled' };
45
45
  try {
@@ -48,16 +48,18 @@ export async function checkpointConversation(application, task, workLog) {
48
48
  events: [{ event_id: `${task.token}:summary`, role: 'summary', kind: 'checkpoint', content: safeConversationContent(workLog.summary) }]
49
49
  });
50
50
  if (result.status !== 'saved') return result;
51
- let name = null;
52
- try {
53
- const agent = await application.personal.getProjectAgent(application.config.personal.spaceId, task.personalProjectId, task.projectAgentId);
54
- name = agent?.profile?.display_name || agent?.profile?.name || null;
55
- } catch { /* Metadata availability does not undo a successful save. */ }
51
+ let name = team?.name ?? null;
52
+ if (!team) {
53
+ try {
54
+ const agent = await application.personal.getProjectAgent(application.config.personal.spaceId, task.personalProjectId, task.projectAgentId);
55
+ name = agent?.profile?.display_name || agent?.profile?.name || null;
56
+ } catch { /* Metadata availability does not undo a successful save. */ }
57
+ }
56
58
  return { ...result, agent_id: task.projectAgentId, work_status: workLog.status,
57
59
  receipt: taskAgentReceipt(application, { projectId: task.personalProjectId,
58
60
  agentId: task.projectAgentId, name, sourceApplication: task.sourceApplication,
59
61
  workStatus: workLog.status, conversationId: result.conversation_id,
60
- persistence: result.status }),
62
+ persistence: result.status, collaborators: team?.collaborators ?? [] }),
61
63
  coverage: 'task_summary; visible transcript capture is confirmed separately by the host hook',
62
64
  guidance: 'Include the Agent name, truthful work status and conversation ID in the final receipt. In another client select this Agent, then call resume_agent_conversation with its current taskContextToken and this conversation ID.' };
63
65
  } catch { return unavailable(); }
@@ -84,6 +86,6 @@ export async function resumeConversation(application, input) {
84
86
  }
85
87
  export async function updateConversationPolicy(application, input) {
86
88
  return application.personal.conversation('policy', { ...conversationScope(application, input), policy: {
87
- idle_days: input.idleDays, context_budget: input.contextBudget, enabled: input.enabled
89
+ compact_after_kb: input.compactAfterKb, context_budget: input.contextBudget, enabled: input.enabled
88
90
  } });
89
91
  }
@@ -1,4 +1,5 @@
1
1
  import { beginConversation, checkpointConversation } from './agent-conversations.js';
2
+ import { taskAgentReceipt } from '../agents/identity-receipt.js';
2
3
  import { createHash } from 'node:crypto';
3
4
 
4
5
  import { ApplicationError, ApplicationErrorCode } from '../app/application-error.js';
@@ -92,7 +93,8 @@ export async function checkpointTaskKnowledge(application, {
92
93
  workLog = null,
93
94
  sourceApplication = 'other',
94
95
  personalProjectId = null,
95
- remoteSessionId = null
96
+ remoteSessionId = null,
97
+ collaborators = []
96
98
  }) {
97
99
  if (!['capture_candidates', 'retain_nothing'].includes(disposition)) {
98
100
  throw validationError('Unknown task knowledge checkpoint disposition');
@@ -122,11 +124,21 @@ export async function checkpointTaskKnowledge(application, {
122
124
  'Task checkpoint already has different input; resume the original review or begin a new task context'
123
125
  );
124
126
  }
127
+ const team = await taskTeam(application, task, collaborators);
128
+ const finish = async (result) => {
129
+ const effectiveLog = workLog ?? { status: 'reported', summary: reason };
130
+ const conversation = await checkpointConversation(application, task, effectiveLog, team);
131
+ return { ...result, conversation,
132
+ agent_receipt: taskAgentReceipt(application, { projectId: task.personalProjectId,
133
+ agentId: task.projectAgentId ?? null, name: team.name, sourceApplication: task.sourceApplication,
134
+ workStatus: effectiveLog.status, conversationId: conversation?.conversation_id ?? null,
135
+ persistence: conversation?.status ?? null, collaborators: team.collaborators }),
136
+ ...(team.ignored.length ? { ignored_collaborators: team.ignored } : {}) };
137
+ };
125
138
  if (task.checkpoint?.phase === 'complete') {
126
- return { status: 'checkpointed', disposition, reason,
139
+ return finish({ status: 'checkpointed', disposition, reason,
127
140
  personal_project_id: task.personalProjectId, project_agent_id: task.projectAgentId,
128
- replayed: true, capture_status: task.checkpoint.captureStatus,
129
- conversation: await checkpointConversation(application, task, workLog ?? { status: 'reported', summary: reason }) };
141
+ replayed: true, capture_status: task.checkpoint.captureStatus });
130
142
  }
131
143
  const captureInput = capture ? {
132
144
  ...capture,
@@ -181,21 +193,36 @@ export async function checkpointTaskKnowledge(application, {
181
193
  await application.taskContextRegistry.checkpoint(taskContextToken, {
182
194
  ...checkpoint, captureStatus: captureResult?.status ?? null
183
195
  }, sourceApplication);
184
- return {
196
+ return finish({
185
197
  status: 'checkpointed',
186
198
  disposition,
187
199
  reason,
188
200
  personal_project_id: task.personalProjectId,
189
201
  project_agent_id: task.projectAgentId ?? null,
190
202
  capture: captureResult,
191
- conversation: await checkpointConversation(application, task, workLog ?? { status: 'reported', summary: reason }),
192
203
  agent_memory: memoryResult ? {
193
204
  status: memoryResult.status, agentId: memoryResult.agentId,
194
205
  checkpointId: memoryResult.checkpointId, revision: memoryResult.revision,
195
206
  sourceApplication: memoryResult.sourceApplication,
196
207
  sourceSessionId: memoryResult.sourceSessionId, createdAt: memoryResult.createdAt
197
208
  } : null
198
- };
209
+ });
210
+ }
211
+
212
+ // Names the lead and only collaborators that are real, active Agents of this space.
213
+ async function taskTeam(application, task, collaboratorIds) {
214
+ const requested = [...new Set(collaboratorIds)].filter(id => id !== task.projectAgentId);
215
+ if (!task.projectAgentId) return { name: null, collaborators: [], ignored: requested };
216
+ let agents = [];
217
+ try {
218
+ agents = await application.listProjectAgents({ personalSpaceId: application.config.personal.spaceId });
219
+ } catch { /* Attribution metadata never blocks a checkpoint. */ }
220
+ const byId = new Map(agents.map(agent => [agent.agentId, agent]));
221
+ const label = agent => agent?.profile.displayName || agent?.profile.name || null;
222
+ const active = requested.filter(id => byId.get(id)?.profile.status === 'active');
223
+ return { name: label(byId.get(task.projectAgentId)),
224
+ collaborators: active.map(id => ({ agentId: id, name: label(byId.get(id)) })),
225
+ ignored: requested.filter(id => !active.includes(id)) };
199
226
  }
200
227
 
201
228
  function canonicalCheckpoint(value) {
@@ -1,6 +1,7 @@
1
- import { dirname } from 'node:path';
1
+ import { dirname, join } from 'node:path';
2
2
  import { createEmployeeService } from '../employees/service.js';
3
- import { createFuliRoundtableService } from '../roundtables/application.js';
3
+ import { createAgentRoundtable } from '../agent-roundtable/service.js';
4
+ import { createRoundtableStore } from '../agent-roundtable/store.js';
4
5
 
5
6
  import { GraphitiProviderClient } from './provider-client.js';
6
7
  import {
@@ -79,6 +80,7 @@ import {
79
80
  } from './related-project-suggestions.js';
80
81
  import { buildUserTasteSkill } from './user-taste-skill.js';
81
82
  import { taskEntryPreferences } from './task-entry-preferences.js';
83
+ import { TASK_ENTRY_TOOLS, registerRepositoryProject } from './repository-project-registration.js';
82
84
  import { getWritingTasteProfile as getWritingTasteProfileWorkflow } from './writing-taste-profile-workflow.js';
83
85
  import {
84
86
  groupSubscriptions,
@@ -117,7 +119,8 @@ export function openFederatedGraphApplication({
117
119
  fetchImpl
118
120
  });
119
121
  app.employees = createEmployeeService({ app, runtimeConfigPath });
120
- app.roundtables = createFuliRoundtableService({ app, dataDir: dirname(runtimeConfigPath) });
122
+ app.roundtable = createAgentRoundtable({ app,
123
+ openStore: () => createRoundtableStore(join(dirname(runtimeConfigPath), 'agent-roundtable.sqlite')) });
121
124
  }
122
125
  return app;
123
126
  }
@@ -258,7 +261,12 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
258
261
  agentInvocation = false,
259
262
  agentToolName = 'get_collaboration_preferences'
260
263
  } = {}) {
261
- const projectResolution = await this.#resolvePreferenceProject({ personalProjectId, projectPath });
264
+ let projectResolution = await this.#resolvePreferenceProject({ personalProjectId, projectPath });
265
+ if (projectResolution.repositoryProjectId && agentInvocation && TASK_ENTRY_TOOLS.has(agentToolName)
266
+ && this.getCapturePolicy().enabled) {
267
+ // Every task gets a FULI Agent, so a task in an unregistered repository registers it.
268
+ projectResolution = await registerRepositoryProject(this, projectResolution);
269
+ }
262
270
  return taskEntryPreferences(this, projectResolution, {
263
271
  personalProjectId, projectAgentId, projectPath, taskPrompt, sourceApplication,
264
272
  sourceSessionId, sessionId, turnId, workKind, requiredCapabilities,
@@ -1152,7 +1160,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
1152
1160
  };
1153
1161
  }
1154
1162
 
1155
- close() { this.roundtables?.close(); return this.employees?.close(); }
1163
+ close() { this.roundtable?.close(); return this.employees?.close(); }
1156
1164
 
1157
1165
  async #recordAgentViews(items, toolName) {
1158
1166
  const unique = new Map(items.map((item) => [
@@ -13,21 +13,33 @@ export async function resolveTaskEntryAgent(application, resolution, input) {
13
13
  const mention = await resolveTaskAgentMention(application, resolution.personalProjectId, input);
14
14
  if (mention?.status) return mention;
15
15
  const workKind = input.workKind ?? taskWorkKind(input.taskPrompt);
16
- const value = await application.personal.resolveProjectAgentContext({
16
+ const scope = {
17
17
  personal_space_id: application.config.personal.spaceId,
18
18
  personal_project_id: resolution.personalProjectId,
19
+ source_application: input.sourceApplication ?? 'other'
20
+ };
21
+ let value = await application.personal.resolveProjectAgentContext({
22
+ ...scope,
19
23
  agent_id: mention?.agentId ?? input.projectAgentId ?? null,
20
24
  session_id: input.sessionId ?? null,
21
25
  turn_id: input.turnId ?? null,
22
26
  work_kind: workKind,
23
- required_capabilities: input.requiredCapabilities ?? [],
24
- source_application: input.sourceApplication ?? 'other'
27
+ required_capabilities: input.requiredCapabilities ?? []
25
28
  });
26
29
  if (!value?.status) throw new Error('Role resolution is unavailable');
27
- const agent = value.agent ? projectAgentRecord(value.agent) : null;
28
- if (agent && !mention?.agentId && !input.projectAgentId && unrelatedPeer(agent, workKind, input.requiredCapabilities)) {
29
- return { status: 'unassigned', reason: 'specialist_required', worker_started: false,
30
- required_action: 'Use coordinate_project_agent_task to select or recruit a qualified durable owner before implementation. HR and project managers collaborate in their own roles; do not attribute development to them by fallback.' };
30
+ let agent = value.agent ? projectAgentRecord(value.agent) : null;
31
+ const implicit = !mention?.agentId && !input.projectAgentId;
32
+ const peerOnly = agent && implicit && unrelatedPeer(agent, workKind, input.requiredCapabilities);
33
+ if (implicit && (peerOnly || value.status === 'unassigned')) {
34
+ // Every task gets a FULI Agent: a project without a lead has HR hire one.
35
+ const staffed = await application.personal.staffDefaultProjectLead(scope).catch(() => null);
36
+ if (staffed?.agent) {
37
+ value = staffed;
38
+ agent = projectAgentRecord(staffed.agent);
39
+ } else if (peerOnly) {
40
+ return { status: 'unassigned', reason: 'specialist_required', worker_started: false,
41
+ required_action: 'Use coordinate_project_agent_task to select or recruit a qualified durable owner before implementation. HR and project managers collaborate in their own roles; do not attribute development to them by fallback.' };
42
+ }
31
43
  }
32
44
  const employeeId = /^employee\.([a-z][a-z0-9-]{0,63})$/.exec(agent?.agentId ?? '')?.[1];
33
45
  if (employeeId && application.employees) {
@@ -47,6 +47,8 @@ export function resolvePersonalProjectPath(projectPath, projects, {
47
47
  fileExists,
48
48
  pathApi
49
49
  });
50
+ // An unregistered repository can be registered under this exact ID.
51
+ let repositoryProjectId = null;
50
52
  if (repositoryRoot) {
51
53
  const worktreeId = worktreeOriginProjectId(
52
54
  pathApi.join(repositoryRoot, '.git'),
@@ -56,10 +58,13 @@ export function resolvePersonalProjectPath(projectPath, projects, {
56
58
  // A worktree's folder name is arbitrary; only its original repository
57
59
  // identifies the project, even when the folder names another project.
58
60
  return resolveProjectId(worktreeId, projectIds, 'git_worktree_origin')
59
- ?? unresolved('unmatched');
61
+ ?? unresolved('unmatched', safeProjectId(worktreeId, pathApi) ? { repositoryProjectId: worktreeId } : {});
60
62
  }
61
63
  const repository = resolveProjectId(pathApi.basename(repositoryRoot), projectIds, 'repository_root');
62
64
  if (repository) return repository;
65
+ if (safeProjectId(pathApi.basename(repositoryRoot), pathApi)) {
66
+ repositoryProjectId = pathApi.basename(repositoryRoot);
67
+ }
63
68
  }
64
69
 
65
70
  const directory = resolveProjectId(pathApi.basename(currentPath), projectIds, 'directory_name');
@@ -76,7 +81,7 @@ export function resolvePersonalProjectPath(projectPath, projects, {
76
81
  if (childMatches.length > 1) {
77
82
  return unresolved('ambiguous', { candidateCount: childMatches.length });
78
83
  }
79
- return unresolved('unmatched');
84
+ return unresolved('unmatched', repositoryProjectId ? { repositoryProjectId } : {});
80
85
  }
81
86
 
82
87
  function resolveProjectId(candidate, projectIds, basis) {
@@ -153,6 +153,14 @@ export class GraphitiProviderClient {
153
153
  resolveProjectAgentContext(input) {
154
154
  return this.#request('/v1/project-agent-context/resolve', { method: 'POST', body: input });
155
155
  }
156
+ recentAgentSessions({ personalSpaceId, agentId, personalProjectId = null, limit = 5 }) {
157
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId, project_agent_id: agentId, limit: String(limit) });
158
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
159
+ return this.#request(`/v1/task-context-sessions/recent?${query}`);
160
+ }
161
+ staffDefaultProjectLead(input) {
162
+ return this.#request('/v1/project-agent-context/default-lead', { method: 'POST', body: input });
163
+ }
156
164
  beginTaskContext(input) {
157
165
  return this.#request('/v1/task-contexts', { method: 'PUT', body: input });
158
166
  }
@@ -0,0 +1,17 @@
1
+ export const TASK_ENTRY_TOOLS = new Set(['begin_task_context', 'get_collaboration_preferences']);
2
+
3
+ // Registers the exact repository ID the path resolver would match next time.
4
+ // A failed registration leaves the task unresolved rather than guessing.
5
+ export async function registerRepositoryProject(application, resolution) {
6
+ const projectId = resolution.repositoryProjectId;
7
+ try {
8
+ await application.upsertPersonalProject({
9
+ personalSpaceId: application.config.personal.spaceId,
10
+ projectId,
11
+ profile: { name: projectId, lifecycle: 'active' }
12
+ });
13
+ } catch {
14
+ return resolution;
15
+ }
16
+ return { status: 'matched', basis: 'registered_repository', personalProjectId: projectId };
17
+ }
@@ -41,7 +41,12 @@ export async function taskEntryPreferences(application, projectResolution, {
41
41
  sourceApplication, taskPrompt, selectionReason: selection.reason,
42
42
  matchBasis: selection.match_basis
43
43
  }) : selection;
44
+ // Questions other Agents left for this Agent while it was not running.
45
+ const waiting = selection?.agent && application.roundtable
46
+ ? application.roundtable.pending(selection.agent.agentId) : [];
44
47
  return { ...managedPreferences, ...(context ? { project_agent_context: context } : {}),
48
+ ...(waiting.length ? { agent_messages: waiting,
49
+ agent_messages_guidance: 'Other Agents are waiting for your answers. Reply to each with reply_agent_message.' } : {}),
45
50
  agent_receipt: taskAgentReceipt(application, {
46
51
  projectId: projectResolution.personalProjectId, agentId: selection?.agent?.agentId ?? null,
47
52
  name: context?.role?.name, sourceApplication
@@ -2,7 +2,8 @@ import { readJson, sendJson } from './response.js';
2
2
  import { handleExternalKnowledgeApiRequest } from './external-knowledge-api-router.js';
3
3
  import { handleGraphApiRequest } from './graph-api-router.js';
4
4
  import { handleEmployeeApiRequest } from './employee-api-router.js';
5
- import { handleRoundtableApiRequest } from '../roundtables/http-router.js';
5
+ import { handleRoundtableApiRequest } from '../agent-roundtable/http.js';
6
+ import { FULI_VERSION } from '../package-metadata.js';
6
7
 
7
8
  export async function handleApiRequest({
8
9
  request,
@@ -13,13 +14,14 @@ export async function handleApiRequest({
13
14
  connectedKnowledge = null
14
15
  }) {
15
16
  const url = new URL(request.url, 'http://127.0.0.1');
16
- if (await handleRoundtableApiRequest({ request, response, url, service: app.roundtables })) return true;
17
+ if (await handleRoundtableApiRequest({ request, response, url, roundtable: app.roundtable })) return true;
17
18
 
18
19
  if (url.pathname === '/api/health' && request.method === 'GET') {
19
20
  sendJson(response, 200, {
20
21
  status: 'ready',
21
22
  service: 'fuli-local-console',
22
- pid: process.pid
23
+ pid: process.pid,
24
+ version: FULI_VERSION
23
25
  });
24
26
  return true;
25
27
  }
@@ -5,7 +5,6 @@ import {
5
5
  import { JsonBodyTooLargeError } from './response.js';
6
6
  import { ProviderRequestError } from '../graphiti/provider-client.js';
7
7
  import { EmployeeError } from '../employees/manifest.js';
8
- import { RoundtableError } from '../roundtables/domain.js';
9
8
 
10
9
  const BAD_REQUEST_CODES = new Set([
11
10
  ApplicationErrorCode.NOT_FOUND,
@@ -13,7 +12,6 @@ const BAD_REQUEST_CODES = new Set([
13
12
  ]);
14
13
 
15
14
  export function mapHttpError(error) {
16
- if (error instanceof RoundtableError) return { status: error.statusCode, body: { error: error.message, code: error.code } };
17
15
  if (error instanceof ApplicationError && ['external_knowledge_busy', 'external_knowledge_conflict'].includes(error.code)) {
18
16
  return { status: 409, body: { error: error.message, code: error.code } };
19
17
  }
@@ -1,11 +1,11 @@
1
1
  export const MCP_INSTRUCTIONS = [
2
- "Final replies MUST copy agent_receipt.markdown unchanged once; no stale owner.",
2
+ "Final replies MUST copy agent_receipt.markdown unchanged once; checkpoint's wins; no stale owner.",
3
3
  "Each user task: begin_task_context hook-provided task context; else before tools/answer call exactly get_collaboration_preferences(projectPath=cwd taskPrompt=current user request); never substitute project action. effective_preferences: personal-global everywhere; matched project+authorized inheritable parent. get_user_taste_skill never replaces user Skill. Resolve deferred_conflict; never guess personalProjectId. Writes=actual payload; final text not compliance.",
4
- "Before asking stable project fact/method: task_knowledge_recall; miss: search_current_project_knowledge focused action/artifact/target/ID queries; never only the full request. Active child first, then inheritable parent; no local_only/RELATED_TO.",
4
+ "Before asking stable project fact/method: task_knowledge_recall; miss: search_current_project_knowledge focused action/artifact/target/ID queries; never only the full request. Active child first, then inheritable parent; no RELATED_TO.",
5
5
  "Before saying unknown: search_knowledge_graph; extra projects=exact IDs. all_local_confirmed only after consent; rg only current repo/workspace. monitoring/Git MCP; search_connected_knowledge; agent_decide response-only.",
6
- "record_decision_trace; errors:record_knowledge_feedback. Batch durable confirmed knowledge: capture_session_knowledge; no secrets. checkpoint_task_knowledge: capture_candidates/retain_nothing+workLog; record_knowledge_usage.",
7
- "coordinate_project_agent_task=isolated; Fuli never spawns; acquire_runtime_lease; run/report; release_runtime_lease in finally.",
6
+ "record_decision_trace; errors:record_knowledge_feedback. Batch durable confirmed knowledge: capture_session_knowledge; no secrets. checkpoint_task_knowledge: capture_candidates/retain_nothing+workLog+collaborators; record_knowledge_usage.",
7
+ "coordinate_project_agent_task; Fuli never spawns; acquire_runtime_lease; release_runtime_lease in finally.",
8
8
  "project_management_context: authorized manager+board; keep specialist; no excluded projects/extra model. Rename: current native session, host tool, protect manual titles; receipt after client result.",
9
- "Wait: all workerStatus terminal. executionSummary: one row/worker: occupation emoji, actual executor/sourceApplication, work/status/session/link, source-labelled cumulative tokens. Missing token usage=-; never invent/estimate/copy totals. Empty: omit; configured/allowed/available not evidence",
9
+ "all workerStatus terminal. executionSummary: one row/worker: occupation emoji, actual executor/sourceApplication, work/status/session/link, source-labelled cumulative tokens. Missing token usage=-; never invent/estimate/copy totals. Empty: omit; configured/allowed/available not evidence",
10
10
  "Terminal-safe Markdown; no HTML. Match=candidate only. If supported: MUST begin sourceMarker.leadMarkdown; append sourceMarker.markdown; else noMatchSourceMarker (empty); Keep unchanged."
11
11
  ].join(' ');
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
4
  const WORKFLOW_OBSERVATION_TOOL = 'record_workflow_transition_observation';
5
5
  const PROJECT_AGENT_SOURCE_TOOLS = new Set([
6
- 'discover_roundtable', 'read_roundtable', 'join_roundtable', 'claim_roundtable_turn', 'submit_roundtable_turn', 'message_roundtable',
6
+ 'find_agents', 'message_agent', 'read_agent_messages', 'reply_agent_message',
7
7
  'get_agent_quality_gate', 'record_agent_verification',
8
8
  'plan_agent_collaboration', 'request_agent_loan', 'decide_agent_loan', 'list_agent_loans',
9
9
  'list_agent_conversations', 'read_agent_conversation', 'resume_agent_conversation',
@@ -1,6 +1,5 @@
1
1
  const READ_TOOLS = new Set([
2
- 'discover_roundtable',
3
- 'read_roundtable',
2
+ 'find_agents', 'read_agent_messages', 'read_agent_thread',
4
3
  'get_agent_quality_gate',
5
4
  'plan_agent_collaboration', 'list_agent_loans',
6
5
  'list_agent_conversations', 'read_agent_conversation', 'get_agent_conversation_policy',
@@ -51,7 +50,7 @@ const READ_TOOLS = new Set([
51
50
  ]);
52
51
 
53
52
  const WRITE_TOOLS = new Set([
54
- 'join_roundtable', 'claim_roundtable_turn', 'submit_roundtable_turn', 'message_roundtable',
53
+ 'message_agent', 'reply_agent_message',
55
54
  'record_agent_verification',
56
55
  'request_agent_loan', 'decide_agent_loan',
57
56
  'resume_agent_conversation', 'update_agent_conversation_policy',
@@ -171,7 +170,7 @@ export function annotationsFor(name) {
171
170
  name === 'record_knowledge_usage' || name === 'record_decision_trace' ||
172
171
  name === 'record_knowledge_feedback' ||
173
172
  name === 'record_workflow_transition_observation' ||
174
- name === 'message_roundtable' ||
173
+ name === 'message_agent' || name === 'reply_agent_message' ||
175
174
  name === 'checkpoint_task_knowledge' ||
176
175
  name === 'checkpoint_project_agent_memory',
177
176
  openWorldHint: name === 'capture_session_knowledge' ||