fuli-context 0.9.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.
- package/README.md +29 -58
- package/README.zh-CN.md +24 -54
- package/dist/web/assets/AboutPage-CKq4IRHd.js +1 -0
- package/dist/web/assets/AgentConversations-DfXiFvBN.js +2 -0
- package/dist/web/assets/AgentConversations-XqNEBY_2.css +1 -0
- package/dist/web/assets/AgentHand-BBM-sKh2.js +1 -0
- package/dist/web/assets/AgentName-C_6R80nP.js +1 -0
- package/dist/web/assets/AgentProfilePage-Cz-i1fRD.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-BI0EFz6y.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-yRWktiin.css +1 -0
- package/dist/web/assets/ConnectionsPage-BoC6kXgs.js +1 -0
- package/dist/web/assets/ConnectionsPage-CM9C-zEI.css +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-BVyclQAJ.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-CVbqCzHb.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-D-c_Jm2z.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-D2GvYraI.js +3 -0
- package/dist/web/assets/KnowledgeEditDialog-a0AKWics.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-hT9znkoe.css +1 -0
- package/dist/web/assets/KnowledgeInspector-DMg9yJZ2.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-BgsHM3Qw.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DeL7Jp34.css +1 -0
- package/dist/web/assets/KnowledgePage-kn8MqUCO.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-BtRq9fvC.js +4 -0
- package/dist/web/assets/KnowledgeWorkspace-D4a7FcAb.css +1 -0
- package/dist/web/assets/NotFoundPage-D-7jLz3o.js +1 -0
- package/dist/web/assets/OverviewPage-BHPc-XxY.js +1 -0
- package/dist/web/assets/OverviewPage-By-fUzSG.css +1 -0
- package/dist/web/assets/PersonalProfilePage-C00iV0Kx.js +1 -0
- package/dist/web/assets/PersonalProfilePage-DrMDAAdy.css +1 -0
- package/dist/web/assets/PersonalProjectsPage-CFNkdHSo.js +1 -0
- package/dist/web/assets/ProjectAgentFirstTask-Bi1ZsTC1.css +1 -0
- package/dist/web/assets/ProjectAgentFirstTask-CrCWEYgn.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-BzztLMH-.js +12 -0
- package/dist/web/assets/ProjectAgentsPage-DNLNjYp-.css +1 -0
- package/dist/web/assets/ProjectScopePicker-CMxn3txv.js +1 -0
- package/dist/web/assets/PublicProjectsPage-DKamXDV9.css +1 -0
- package/dist/web/assets/PublicProjectsPage-DQVtvhOS.js +1 -0
- package/dist/web/assets/ReviewPage-BZhvyDWc.js +1 -0
- package/dist/web/assets/RoundtablesPage-CVebTXQa.js +1 -0
- package/dist/web/assets/RoundtablesPage-Cb0R4GaM.css +1 -0
- package/dist/web/assets/SettingsPage-1knvfOW6.js +1 -0
- package/dist/web/assets/SettingsPage-BmYkLul4.css +1 -0
- package/dist/web/assets/SharedJefaReportPage-BCVq8u0P.js +1 -0
- package/dist/web/assets/UiDialog-Da1P10f4.js +1 -0
- package/dist/web/assets/UiDisclosure-8wE8YuE4.js +1 -0
- package/dist/web/assets/UiSegmented-BKZXc8QF.js +1 -0
- package/dist/web/assets/UiSelect-Bv6R0yr1.css +1 -0
- package/dist/web/assets/UiSelect-D5Xj5Ec4.js +1 -0
- package/dist/web/assets/WritingTastePage-B8-etOBn.js +1 -0
- package/dist/web/assets/WritingTastePage-B_zYD1df.css +1 -0
- package/dist/web/assets/agent-profile-BZj3ezWe.css +1 -0
- package/dist/web/assets/{attention-store-Dk-_P4Oi.js → attention-store-BfxtL9EV.js} +1 -1
- package/dist/web/assets/{catalog-DN72452f.js → catalog-6SN9i3WR.js} +1 -1
- package/dist/web/assets/client-CUTkF3t4.js +1 -0
- package/dist/web/assets/{console-BtPqo79j.js → console-CG8tdtSs.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-jkHRmWai.js → conversation-launcher-settings-CyZkLJg7.js} +1 -1
- package/dist/web/assets/{i18n-D2CJkEYK.js → i18n-CCMR4gDS.js} +3 -3
- package/dist/web/assets/{identity-B3zEw-1u.js → identity-C1eN-FSB.js} +1 -1
- package/dist/web/assets/index-kPB0Cf8Q.css +1 -0
- package/dist/web/assets/index-sLDKI6R-.js +5 -0
- package/dist/web/assets/{model-S7ehAjky.js → model-l-sAqtW_.js} +1 -1
- package/dist/web/assets/{personal-profile-graph--GG3Mizo.js → personal-profile-graph-BD1Y6TDS.js} +1 -1
- package/dist/web/assets/shared-report-B46dxlCR.js +1 -0
- package/dist/web/assets/{useAgentRoster-BAPKWZ9y.js → useAgentRoster-DdxoKDEE.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-BtFMXFjw.js → useApi-CROJJdhE-DlOiu8lP.js} +1 -1
- package/dist/web/assets/{vue-router-DkaTtPn0.js → vue-router-DjqEU4W2.js} +1 -1
- package/dist/web/index.html +14 -16
- package/docs/agent-conversations-and-collaboration.md +9 -9
- package/docs/agent-roundtable.md +61 -0
- package/docs/employee-agents.md +2 -2
- package/docs/project-agent-memory.md +1 -1
- package/graph-provider/fuli_graph/agent_conversation_models.py +11 -2
- package/graph-provider/fuli_graph/conversation_context.py +40 -9
- package/graph-provider/fuli_graph/default_project_lead.py +88 -0
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +7 -0
- package/graph-provider/fuli_graph/project_agent_task_models.py +3 -0
- package/graph-provider/fuli_graph/runtime.py +3 -76
- package/graph-provider/fuli_graph/store.py +2 -0
- package/graph-provider/fuli_graph/store_agent_conversations.py +60 -8
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +3 -1
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +12 -8
- package/graph-provider/fuli_graph/store_project_agents.py +25 -124
- package/graph-provider/fuli_graph/store_task_contexts.py +26 -0
- package/graph-provider/fuli_graph/system_agents.py +128 -0
- package/graph-provider/fuli_graph/system_hr_identity.py +1 -1
- package/graph-provider/fuli_graph/task_context_routes.py +10 -0
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -4
- package/skills/capturing-session-knowledge/SKILL.md +1 -1
- package/src/agent-roundtable/http.js +14 -0
- package/src/agent-roundtable/service.js +218 -0
- package/src/agent-roundtable/store.js +86 -0
- package/src/agent-roundtable/tools.js +61 -0
- package/src/agent-roundtable/wake.js +99 -0
- package/src/agent-tools/agent-conversation-definitions.js +3 -3
- package/src/agent-tools/graph-definitions.js +3 -2
- package/src/agent-tools/interface-catalog.js +0 -5
- package/src/agent-tools.js +2 -2
- package/src/agents/claude-code/lifecycle-hook.js +6 -19
- package/src/agents/claude-code/wake.js +37 -0
- package/src/agents/codex/lifecycle-hook.js +51 -27
- package/src/agents/codex/token-usage.js +235 -0
- package/src/agents/codex/wake.js +47 -0
- package/src/agents/cursor/lifecycle-hook.js +3 -9
- package/src/agents/identity-receipt.js +16 -6
- package/src/agents/session-log.js +34 -0
- package/src/agents/token-usage-resolver.js +147 -0
- package/src/agents/wake-registry.js +14 -0
- package/src/cli/command-registry.js +0 -2
- package/src/cli/setup-command.js +2 -5
- package/src/cli/update-command.js +13 -2
- package/src/cli.js +0 -5
- package/src/employees/management-policy.js +4 -2
- package/src/employees/manifest.js +3 -0
- package/src/employees/recruitment.js +3 -2
- package/src/graphiti/agent-conversations.js +11 -9
- package/src/graphiti/agent-knowledge-workflows.js +34 -7
- package/src/graphiti/federated-application.js +18 -6
- package/src/graphiti/project-agent-mapping.js +1 -1
- package/src/graphiti/project-agent-task-entry.js +19 -7
- package/src/graphiti/project-agent-workflows.js +6 -1
- package/src/graphiti/project-path-context.js +7 -2
- package/src/graphiti/provider-client.js +8 -0
- package/src/graphiti/repository-project-registration.js +17 -0
- package/src/graphiti/task-entry-preferences.js +5 -0
- package/src/http/api-router.js +5 -3
- package/src/http/error-mapping.js +0 -2
- package/src/mcp/instructions.js +5 -5
- package/src/mcp/session-id.js +1 -1
- package/src/mcp/tool-annotations.js +3 -2
- package/src/setup/agents.js +1 -41
- package/src/setup/graph-runtime.js +15 -2
- package/src/setup/session-skill.js +22 -2
- package/dist/web/assets/AboutPage-D-GyJNv1.js +0 -1
- package/dist/web/assets/AgentConversations-DOuZgUeA.js +0 -2
- package/dist/web/assets/AgentConversations-DS8h3yKn.css +0 -1
- package/dist/web/assets/AgentHand-eGsXoGSq.js +0 -1
- package/dist/web/assets/AgentName-Dtmu9Ff5.js +0 -1
- package/dist/web/assets/AgentProfilePage-D8Tvayn7.js +0 -1
- package/dist/web/assets/AgentsDirectoryPage-BTEfKZjF.js +0 -1
- package/dist/web/assets/ConnectionsPage-C1w99yN3.css +0 -1
- package/dist/web/assets/ConnectionsPage-DwOIcvl9.js +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-BrIMRblq.css +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-D11blfwU.js +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-BcEzYt65.css +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-CK2BAoGt.js +0 -3
- package/dist/web/assets/GrowthLoading-KTjE2e50.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-B3x8vs3c.css +0 -1
- package/dist/web/assets/KnowledgeEditDialog-n9trHQ5d.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BcNjgw9H.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-BzgxP-nG.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DMBpGxLr.js +0 -1
- package/dist/web/assets/KnowledgePage-POkCMW8f.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-2FvQadhB.js +0 -4
- package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +0 -1
- package/dist/web/assets/NotFoundPage-BCMZbeB8.js +0 -1
- package/dist/web/assets/OverviewPage-0CFWCG9-.css +0 -1
- package/dist/web/assets/OverviewPage-0mYifpjp.js +0 -1
- package/dist/web/assets/PersonalProfilePage-Chv2a2pi.css +0 -1
- package/dist/web/assets/PersonalProfilePage-JW6Muix6.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CRbi64JP.js +0 -1
- package/dist/web/assets/ProjectAgentFirstTask-6afJ8HUe.css +0 -1
- package/dist/web/assets/ProjectAgentFirstTask-DJ0wTbh8.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-BgQLIpxg.css +0 -1
- package/dist/web/assets/ProjectAgentsPage-CQemHFxy.js +0 -12
- package/dist/web/assets/ProjectScopePicker-BwB7gK-7.js +0 -1
- package/dist/web/assets/PublicProjectsPage-CB272Sq-.js +0 -1
- package/dist/web/assets/ReviewPage-CelTZU4C.js +0 -1
- package/dist/web/assets/RoundtablesPage-ByVeZlgC.js +0 -2
- package/dist/web/assets/RoundtablesPage-DCbIBnBq.css +0 -1
- package/dist/web/assets/SearchableSelect-f7EHbyDL.js +0 -1
- package/dist/web/assets/SettingsPage-5g7mRN2F.css +0 -1
- package/dist/web/assets/SettingsPage-nAijmm5f.js +0 -1
- package/dist/web/assets/SharedJefaReportPage-CjpJ9bkk.js +0 -1
- package/dist/web/assets/UiDisclosure-CWlC91Zt.js +0 -1
- package/dist/web/assets/WritingTastePage-DaHWY61P.js +0 -1
- package/dist/web/assets/WritingTastePage-SMGF39mf.css +0 -1
- package/dist/web/assets/agent-profile-CBvJHGjt.css +0 -1
- package/dist/web/assets/client-B2cRl-7c.js +0 -1
- package/dist/web/assets/copy-D2LqwtPZ.js +0 -1
- package/dist/web/assets/index-CNp0tRNp.css +0 -1
- package/dist/web/assets/index-UygXwZDZ.js +0 -5
- package/dist/web/assets/shared-report-DEK9wvhl.js +0 -1
- package/dist/web/assets/useModalDialog-D8tiJfbc.js +0 -1
- package/docs/roundtable-beta-testing.md +0 -102
- package/docs/roundtable-beta-validation.md +0 -133
- package/docs/roundtable-research.md +0 -142
- package/src/agents/a2a/roundtable-participant.js +0 -374
- package/src/agents/claude-code/roundtable-participant.js +0 -24
- package/src/agents/codex/roundtable-config.js +0 -32
- package/src/agents/codex/roundtable-participant.js +0 -34
- package/src/agents/grok/roundtable-participant.js +0 -185
- package/src/agents/pi/roundtable-participant.js +0 -193
- package/src/agents/pi/roundtable-result-format.js +0 -183
- package/src/cli/roundtable-command.js +0 -79
- package/src/roundtables/application.js +0 -69
- package/src/roundtables/domain.js +0 -181
- package/src/roundtables/http-router.js +0 -56
- package/src/roundtables/mcp-server.js +0 -48
- package/src/roundtables/participant-registry.js +0 -12
- package/src/roundtables/process.js +0 -70
- package/src/roundtables/result-contract.js +0 -47
- package/src/roundtables/server.js +0 -52
- package/src/roundtables/service.js +0 -396
- package/src/roundtables/store.js +0 -59
- package/src/roundtables/tool-contract.js +0 -41
- package/src/roundtables/turn-prompt.js +0 -24
- package/src/roundtables/worker.js +0 -85
- /package/dist/web/assets/{GrowthLoading-CIuQ1icy.css → client-CIuQ1icy.css} +0 -0
|
@@ -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]
|
package/src/cli/setup-command.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
@@ -92,6 +94,7 @@ export function openFederatedGraphApplication({
|
|
|
92
94
|
config,
|
|
93
95
|
capturePolicyStore,
|
|
94
96
|
agentAccessPolicyStore,
|
|
97
|
+
tokenUsageResolver,
|
|
95
98
|
fetchImpl = globalThis.fetch,
|
|
96
99
|
env = process.env
|
|
97
100
|
}) {
|
|
@@ -105,6 +108,7 @@ export function openFederatedGraphApplication({
|
|
|
105
108
|
agentAccessPolicyStore: agentAccessPolicyStore ?? new AgentAccessPolicyStore(
|
|
106
109
|
agentAccessPolicyPathForRuntime(runtimeConfigPath)
|
|
107
110
|
),
|
|
111
|
+
tokenUsageResolver,
|
|
108
112
|
consoleUrl: sourceConsoleUrl(runtimeConfigPath),
|
|
109
113
|
providerRequestTimeoutMs: providerRequestTimeoutFromEnv(env)
|
|
110
114
|
});
|
|
@@ -115,7 +119,8 @@ export function openFederatedGraphApplication({
|
|
|
115
119
|
fetchImpl
|
|
116
120
|
});
|
|
117
121
|
app.employees = createEmployeeService({ app, runtimeConfigPath });
|
|
118
|
-
app.
|
|
122
|
+
app.roundtable = createAgentRoundtable({ app,
|
|
123
|
+
openStore: () => createRoundtableStore(join(dirname(runtimeConfigPath), 'agent-roundtable.sqlite')) });
|
|
119
124
|
}
|
|
120
125
|
return app;
|
|
121
126
|
}
|
|
@@ -140,7 +145,8 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
140
145
|
consoleUrl = sourceConsoleUrl(null),
|
|
141
146
|
projectPathResolver = resolvePersonalProjectPath,
|
|
142
147
|
taskContextRegistry = null,
|
|
143
|
-
providerRequestTimeoutMs = undefined
|
|
148
|
+
providerRequestTimeoutMs = undefined,
|
|
149
|
+
tokenUsageResolver = undefined
|
|
144
150
|
} = {}) {
|
|
145
151
|
super();
|
|
146
152
|
this.graphiti = true;
|
|
@@ -149,6 +155,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
149
155
|
this.agentAccessPolicyStore = agentAccessPolicyStore;
|
|
150
156
|
this.consoleUrl = consoleUrl;
|
|
151
157
|
this.projectPathResolver = projectPathResolver;
|
|
158
|
+
this.tokenUsageResolver = tokenUsageResolver;
|
|
152
159
|
this.personal = new GraphitiProviderClient({
|
|
153
160
|
baseUrl: config.personal.providerUrl,
|
|
154
161
|
accessToken: config.personal.accessToken,
|
|
@@ -254,7 +261,12 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
254
261
|
agentInvocation = false,
|
|
255
262
|
agentToolName = 'get_collaboration_preferences'
|
|
256
263
|
} = {}) {
|
|
257
|
-
|
|
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
|
+
}
|
|
258
270
|
return taskEntryPreferences(this, projectResolution, {
|
|
259
271
|
personalProjectId, projectAgentId, projectPath, taskPrompt, sourceApplication,
|
|
260
272
|
sourceSessionId, sessionId, turnId, workKind, requiredCapabilities,
|
|
@@ -1148,7 +1160,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
1148
1160
|
};
|
|
1149
1161
|
}
|
|
1150
1162
|
|
|
1151
|
-
close() { this.
|
|
1163
|
+
close() { this.roundtable?.close(); return this.employees?.close(); }
|
|
1152
1164
|
|
|
1153
1165
|
async #recordAgentViews(items, toolName) {
|
|
1154
1166
|
const unique = new Map(items.map((item) => [
|
|
@@ -380,7 +380,7 @@ export function providerProjectAgentTaskActivity(input) {
|
|
|
380
380
|
}
|
|
381
381
|
if (hasAny(input, ['tokenUsage', 'token_usage'])) {
|
|
382
382
|
result.token_usage = providerProjectAgentTokenUsage(
|
|
383
|
-
input.tokenUsage
|
|
383
|
+
input.tokenUsage !== undefined ? input.tokenUsage : input.token_usage
|
|
384
384
|
);
|
|
385
385
|
}
|
|
386
386
|
if (hasAny(input, ['workerRuntime', 'worker_runtime'])) {
|
|
@@ -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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { loadProjectAgentContinuity } from './project-agent-task-entry.js';
|
|
2
2
|
import { resolveCoordinationScope, scopedTaskIdempotencyKey } from './temporary-project-scope.js';
|
|
3
|
+
import { enrichProjectAgentTaskActivity } from '../agents/token-usage-resolver.js';
|
|
3
4
|
import {
|
|
4
5
|
executorActualReportRecord,
|
|
5
6
|
executorRecord,
|
|
@@ -306,8 +307,12 @@ export async function viewProjectAgentTask(application, input) {
|
|
|
306
307
|
}
|
|
307
308
|
|
|
308
309
|
export async function recordProjectAgentTaskActivity(application, input) {
|
|
310
|
+
const activity = await enrichProjectAgentTaskActivity(
|
|
311
|
+
input,
|
|
312
|
+
application.tokenUsageResolver
|
|
313
|
+
);
|
|
309
314
|
const value = await application.personal.recordProjectAgentTaskActivity(
|
|
310
|
-
providerProjectAgentTaskActivity(
|
|
315
|
+
providerProjectAgentTaskActivity(activity)
|
|
311
316
|
);
|
|
312
317
|
return value?.task ? projectAgentTaskRecord(value.task) : projectAgentTaskRecord(value);
|
|
313
318
|
}
|
|
@@ -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
|
package/src/http/api-router.js
CHANGED
|
@@ -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 '../
|
|
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,
|
|
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
|
}
|
package/src/mcp/instructions.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
"
|
|
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(' ');
|
package/src/mcp/session-id.js
CHANGED
|
@@ -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
|
-
'
|
|
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,5 +1,5 @@
|
|
|
1
1
|
const READ_TOOLS = new Set([
|
|
2
|
-
'
|
|
2
|
+
'find_agents', 'read_agent_messages', 'read_agent_thread',
|
|
3
3
|
'get_agent_quality_gate',
|
|
4
4
|
'plan_agent_collaboration', 'list_agent_loans',
|
|
5
5
|
'list_agent_conversations', 'read_agent_conversation', 'get_agent_conversation_policy',
|
|
@@ -50,7 +50,7 @@ const READ_TOOLS = new Set([
|
|
|
50
50
|
]);
|
|
51
51
|
|
|
52
52
|
const WRITE_TOOLS = new Set([
|
|
53
|
-
'
|
|
53
|
+
'message_agent', 'reply_agent_message',
|
|
54
54
|
'record_agent_verification',
|
|
55
55
|
'request_agent_loan', 'decide_agent_loan',
|
|
56
56
|
'resume_agent_conversation', 'update_agent_conversation_policy',
|
|
@@ -170,6 +170,7 @@ export function annotationsFor(name) {
|
|
|
170
170
|
name === 'record_knowledge_usage' || name === 'record_decision_trace' ||
|
|
171
171
|
name === 'record_knowledge_feedback' ||
|
|
172
172
|
name === 'record_workflow_transition_observation' ||
|
|
173
|
+
name === 'message_agent' || name === 'reply_agent_message' ||
|
|
173
174
|
name === 'checkpoint_task_knowledge' ||
|
|
174
175
|
name === 'checkpoint_project_agent_memory',
|
|
175
176
|
openWorldHint: name === 'capture_session_knowledge' ||
|